From 4cdb4bdad966fcfeb76548f5f62307fbdfb93e40 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Jul 2022 17:33:27 +0300 Subject: [PATCH 001/132] Hack apron unassume Kind of works internally, but still fails due to widening since sync isn't applied when loop is first reached. --- foo.c | 10 ++++++++++ src/analyses/apron/apronAnalysis.apron.ml | 21 +++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 foo.c diff --git a/foo.c b/foo.c new file mode 100644 index 0000000000..b193d66f7d --- /dev/null +++ b/foo.c @@ -0,0 +1,10 @@ +#include + +int main() { + int i = 0; + while (i < 100) { + i++; + } + assert(i == 100); + return 0; +} \ No newline at end of file diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 9e760a5c14..9e507ca58d 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -546,6 +546,23 @@ struct st let sync ctx reason = + let apr = match ctx.node with + | Statement {sid=82; _} -> + let fd = Node.find_fundec ctx.node in + ignore (Pretty.printf "@@82: %a\n" D.pretty ctx.local); + let a = AD.bot () in + let ax = Var.of_string "i#1623" in + let a = AD.add_vars a [ax] in + let x = ApronDomain.V.to_cil_varinfo fd ax |> Option.get in + let a = AD.assert_inv a Cil.(BinOp (LAnd, BinOp (Ge, Lval (var x), integer 0, intType), BinOp (Lt, Lval (var x), integer 100, intType), intType)) false in + ignore (Pretty.printf "%a\n" AD.pretty a); + let a' = AD.join ctx.local.apr a in + ignore (Pretty.printf "%a\n" AD.pretty a'); + a' + | _ -> + ctx.local.apr + in + (* After the solver is finished, store the results (for later comparison) *) if !GU.postsolving then begin let keep_local = GobConfig.get_bool "ana.apron.invariant.local" in @@ -557,13 +574,13 @@ struct | Some Local -> keep_local | _ -> false in - let st = keep_filter ctx.local.apr var_filter in + let st = keep_filter apr var_filter in let old_value = RH.find_default results ctx.node (AD.bot ()) in let new_value = AD.join old_value st in RH.replace results ctx.node new_value; end; - 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 with apr} (reason :> [`Normal | `Join | `Return | `Init | `Thread]) let init marshal = Priv.init () From b151c620dfb457d24b2a9c672d6a2cae81aa7dea Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 10:43:38 +0300 Subject: [PATCH 002/132] Use event to trigger unassume after each incoming edge This triggers unassume already on loop entry edge and avoids widening. --- src/analyses/apron/apronAnalysis.apron.ml | 25 +++++++++++++++++++++-- src/domains/events.ml | 2 ++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 9e507ca58d..34987512bd 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -178,6 +178,7 @@ struct (* Basic transfer functions. *) let assign ctx (lv:lval) e = + ctx.emit Unassume; let st = ctx.local in if !GU.global_initialization && e = MyCFG.unknown_exp then st (* ignore extern inits because there's no body before assign, so the apron env is empty... *) @@ -542,11 +543,29 @@ struct Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st | Events.Escape escaped -> Priv.escape ctx.node (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st escaped + | Events.Unassume -> + let apr = match ctx.node with + | Statement {sid=82; _} -> + let fd = Node.find_fundec ctx.node in + ignore (Pretty.printf "@@82: %a\n" D.pretty ctx.local); + let a = AD.bot () in + let ax = Var.of_string "i#1623" in + let a = AD.add_vars a [ax] in + let x = ApronDomain.V.to_cil_varinfo fd ax |> Option.get in + let a = AD.assert_inv a Cil.(BinOp (LAnd, BinOp (Ge, Lval (var x), integer 0, intType), BinOp (Le, Lval (var x), integer 100, intType), intType)) false in + ignore (Pretty.printf "%a\n" AD.pretty a); + let a' = AD.join ctx.local.apr a in + ignore (Pretty.printf "%a\n" AD.pretty a'); + a' + | _ -> + ctx.local.apr + in + {ctx.local with apr} | _ -> st let sync ctx reason = - let apr = match ctx.node with + (* let apr = match ctx.node with | Statement {sid=82; _} -> let fd = Node.find_fundec ctx.node in ignore (Pretty.printf "@@82: %a\n" D.pretty ctx.local); @@ -561,7 +580,9 @@ struct a' | _ -> ctx.local.apr - in + in *) + let apr = ctx.local.apr in + (* ctx.emit Unassume; *) (* After the solver is finished, store the results (for later comparison) *) if !GU.postsolving then begin diff --git a/src/domains/events.ml b/src/domains/events.ml index a02803b540..7b5c17c3a5 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -9,6 +9,7 @@ type t = | AssignSpawnedThread of lval * ThreadIdDomain.Thread.t (** Assign spawned thread's ID to lval. *) | Access of {var_opt: CilType.Varinfo.t option; kind: AccessKind.t} (** Access varinfo (unknown if None). *) | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) + | Unassume let pretty () = function | Lock m -> dprintf "Lock %a" LockDomain.Lockset.Lock.pretty m @@ -19,3 +20,4 @@ let pretty () = function | AssignSpawnedThread (lval, tid) -> dprintf "AssignSpawnedThread (%a, %a)" d_lval lval ThreadIdDomain.Thread.pretty tid | Access {var_opt; kind} -> dprintf "Access {var_opt=%a, kind=%a}" (docOpt (CilType.Varinfo.pretty ())) var_opt AccessKind.pretty kind | Assign {lval; exp} -> dprintf "Assign {lval=%a, exp=%a}" CilType.Lval.pretty lval CilType.Exp.pretty exp + | Unassume -> dprintf "Unassume" From 692eb844b4feb6fb05a255bf1dd379e93962c11b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 11:25:01 +0300 Subject: [PATCH 003/132] Add MyCFG.current_cfg --- src/framework/control.ml | 1 + src/framework/myCFG.ml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/framework/control.ml b/src/framework/control.ml index b3b21caac1..a65cc801e5 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -711,4 +711,5 @@ let compute_cfg file = let analyze change_info (file: file) fs = if (get_bool "dbg.verbose") then print_endline "Generating the control flow graph."; let (module CFG) = compute_cfg file in + MyCFG.current_cfg := (module CFG); analyze_loop (module CFG) file fs change_info diff --git a/src/framework/myCFG.ml b/src/framework/myCFG.ml index 910f1e499a..0ae32c64f9 100644 --- a/src/framework/myCFG.ml +++ b/src/framework/myCFG.ml @@ -45,6 +45,14 @@ module NodeH = BatHashtbl.Make (Node) let current_node : node option ref = ref None +let current_cfg : (module CfgBidir) ref = + let module Cfg = + struct + let next _ = raise Not_found + let prev _ = raise Not_found + end + in + ref (module Cfg: CfgBidir) let unknown_exp : exp = mkString "__unknown_value__" let dummy_func = emptyFunction "__goblint_dummy_init" (* TODO get rid of this? *) From 48503c69027735478af44add4b49b3daa3ea0065 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 11:53:44 +0300 Subject: [PATCH 004/132] Add unassume analysis to read YAML witnesses --- src/analyses/unassumeAnalysis.ml | 109 +++++++++++++++++++++++++++++++ src/util/options.schema.json | 6 ++ 2 files changed, 115 insertions(+) create mode 100644 src/analyses/unassumeAnalysis.ml diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml new file mode 100644 index 0000000000..4b3afad4aa --- /dev/null +++ b/src/analyses/unassumeAnalysis.ml @@ -0,0 +1,109 @@ +(** Unassume analysis. *) +open Analyses + +module NH = CfgTools.NH + +module Spec = +struct + include UnitAnalysis.Spec + let name () = "unassume" + + module Locator = WitnessUtil.Locator (Node) + + let locator: Locator.t ref = ref (Locator.create ()) (* empty default, so don't have to use option everywhere *) + + let invs: Cil.exp NH.t = NH.create 100 + + let init _ = + locator := Locator.create (); (* TODO: add Locator.clear *) + let module Cfg = (val !MyCFG.current_cfg) in + + (* DFS, copied from CfgTools.find_backwards_reachable *) + let reachable = NH.create 100 in + let rec iter_node node = + if not (NH.mem reachable node) then begin + NH.replace reachable node (); + Locator.add !locator (Node.location node) node; + List.iter (fun (_, prev_node) -> + iter_node prev_node + ) (Cfg.prev node) + end + in + + Cil.iterGlobals !Cilfacade.current_file (function + | GFun (fd, _) -> + let return_node = Node.Function fd in + iter_node return_node + | _ -> () + ); + + let loc_of_location (location: YamlWitnessType.Location.t): Cil.location = { + file = location.file_name; + line = location.line; + column = location.column + 1; + byte = -1; + endLine = -1; + endColumn = -1; + endByte = -1; + synthetic = false; + } + in + + let yaml = Yaml_unix.of_file_exn (Fpath.v (GobConfig.get_string "witness.yaml.unassume")) in + let yaml_entries = yaml |> GobYaml.list |> BatResult.get_ok in + + let module InvariantParser = WitnessUtil.InvariantParser in + let inv_parser = InvariantParser.create !Cilfacade.current_file in + + NH.clear invs; + + let unassume_entry (entry: YamlWitnessType.Entry.t) = + + let unassume_nodes_invariant ~loc ~nodes inv = + match InvariantParser.parse_cabs inv with + | Ok inv_cabs -> + + Locator.ES.iter (fun n -> + let fundec = Node.find_fundec n in + + match InvariantParser.parse_cil inv_parser ~fundec ~loc inv_cabs with + | Ok inv_exp -> + M.debug ~category:Witness ~loc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; + NH.add invs n inv_exp + | Error e -> + M.error ~category:Witness ~loc "CIL couldn't parse invariant: %s" inv; + M.info ~category:Witness ~loc "invariant has undefined variables or side effects: %s" inv + ) nodes; + + | Error e -> + M.error ~category:Witness ~loc "Frontc couldn't parse invariant: %s" inv; + M.info ~category:Witness ~loc "invariant has invalid syntax: %s" inv + in + + let unassume_loop_invariant (loop_invariant: YamlWitnessType.LoopInvariant.t) = + let loc = loc_of_location loop_invariant.location in + let inv = loop_invariant.loop_invariant.string in + + match Locator.find_opt !locator loc with + | Some nodes -> + unassume_nodes_invariant ~loc ~nodes inv + | None -> + M.warn ~category:Witness ~loc "couldn't locate invariant: %s" inv + in + + match entry.entry_type with + | LoopInvariant x -> + unassume_loop_invariant x + | entry_type -> + M.info_noloc ~category:Witness "cannot unassume entry of type %s" (YamlWitnessType.EntryType.entry_type entry_type) + in + + List.iter (fun yaml_entry -> + match YamlWitnessType.Entry.of_yaml yaml_entry with + | Ok entry -> unassume_entry entry + | Error (`Msg e) -> M.info_noloc ~category:Witness "couldn't parse entry: %s" e + ) yaml_entries +end + +let _ = + MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/util/options.schema.json b/src/util/options.schema.json index e27d2d60a4..56a59fba1a 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -1887,6 +1887,12 @@ "type": "string", "default": "" }, + "unassume": { + "title": "witness.yaml.unassume", + "description": "YAML witness input path", + "type": "string", + "default": "" + }, "certificate": { "title": "witness.yaml.certificate", "description": "YAML witness certificate output path", From 175cd53f1c322ae064c9e1f2256a5b3e61df30c3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 12:15:31 +0300 Subject: [PATCH 005/132] Unassume witness invariants in apron analysis --- foo.c | 10 ---- src/analyses/apron/apronAnalysis.apron.ml | 28 +++++----- src/analyses/unassumeAnalysis.ml | 8 +++ src/domains/events.ml | 4 +- .../56-witness/10-apron-unassume-interval.c | 11 ++++ .../56-witness/10-apron-unassume-interval.yml | 56 +++++++++++++++++++ 6 files changed, 91 insertions(+), 26 deletions(-) delete mode 100644 foo.c create mode 100644 tests/regression/56-witness/10-apron-unassume-interval.c create mode 100644 tests/regression/56-witness/10-apron-unassume-interval.yml diff --git a/foo.c b/foo.c deleted file mode 100644 index b193d66f7d..0000000000 --- a/foo.c +++ /dev/null @@ -1,10 +0,0 @@ -#include - -int main() { - int i = 0; - while (i < 100) { - i++; - } - assert(i == 100); - return 0; -} \ No newline at end of file diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 34987512bd..2120527624 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -178,7 +178,6 @@ struct (* Basic transfer functions. *) let assign ctx (lv:lval) e = - ctx.emit Unassume; let st = ctx.local in if !GU.global_initialization && e = MyCFG.unknown_exp then st (* ignore extern inits because there's no body before assign, so the apron env is empty... *) @@ -543,22 +542,23 @@ struct Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st | Events.Escape escaped -> Priv.escape ctx.node (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st escaped - | Events.Unassume -> - let apr = match ctx.node with - | Statement {sid=82; _} -> - let fd = Node.find_fundec ctx.node in - ignore (Pretty.printf "@@82: %a\n" D.pretty ctx.local); + | Events.Unassume e -> + let apr = + (* let fd = Node.find_fundec ctx.node in *) + (* ignore (Pretty.printf "@@82 %a: %a\n" d_exp e D.pretty ctx.local); *) let a = AD.bot () in - let ax = Var.of_string "i#1623" in - let a = AD.add_vars a [ax] in - let x = ApronDomain.V.to_cil_varinfo fd ax |> Option.get in - let a = AD.assert_inv a Cil.(BinOp (LAnd, BinOp (Ge, Lval (var x), integer 0, intType), BinOp (Le, Lval (var x), integer 100, intType), intType)) false in - ignore (Pretty.printf "%a\n" AD.pretty a); + (* let ax = Var.of_string "i#1623" in *) + (* let a = AD.add_vars a [ax] in *) + let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal in + let a = AD.add_vars a (vars |> List.map V.local) in + let a = List.fold_left assert_type_bounds a vars in (* type bounds to avoid overflow in top state *) + (* let x = ApronDomain.V.to_cil_varinfo fd ax |> Option.get in *) + (* let a = AD.assert_inv a Cil.(BinOp (LAnd, BinOp (Ge, Lval (var x), integer 0, intType), BinOp (Le, Lval (var x), integer 100, intType), intType)) false in *) + let a = AD.assert_inv a e false in + (* ignore (Pretty.printf "%a\n" AD.pretty a); *) let a' = AD.join ctx.local.apr a in - ignore (Pretty.printf "%a\n" AD.pretty a'); + (* ignore (Pretty.printf "%a\n" AD.pretty a'); *) a' - | _ -> - ctx.local.apr in {ctx.local with apr} | _ -> diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 4b3afad4aa..4e315ecc82 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -103,6 +103,14 @@ struct | Ok entry -> unassume_entry entry | Error (`Msg e) -> M.info_noloc ~category:Witness "couldn't parse entry: %s" e ) yaml_entries + + let assign ctx lv e = + match NH.find_all invs ctx.node with + | x :: xs -> + let e = List.fold_left (fun a b -> Cil.(BinOp (LAnd, a, b, intType))) x xs in + ctx.emit (Unassume e) + | [] -> + () end let _ = diff --git a/src/domains/events.ml b/src/domains/events.ml index 7b5c17c3a5..8833359fb6 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -9,7 +9,7 @@ type t = | AssignSpawnedThread of lval * ThreadIdDomain.Thread.t (** Assign spawned thread's ID to lval. *) | Access of {var_opt: CilType.Varinfo.t option; kind: AccessKind.t} (** Access varinfo (unknown if None). *) | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) - | Unassume + | Unassume of exp let pretty () = function | Lock m -> dprintf "Lock %a" LockDomain.Lockset.Lock.pretty m @@ -20,4 +20,4 @@ let pretty () = function | AssignSpawnedThread (lval, tid) -> dprintf "AssignSpawnedThread (%a, %a)" d_lval lval ThreadIdDomain.Thread.pretty tid | Access {var_opt; kind} -> dprintf "Access {var_opt=%a, kind=%a}" (docOpt (CilType.Varinfo.pretty ())) var_opt AccessKind.pretty kind | Assign {lval; exp} -> dprintf "Assign {lval=%a, exp=%a}" CilType.Lval.pretty lval CilType.Exp.pretty exp - | Unassume -> dprintf "Unassume" + | Unassume e -> dprintf "Unassume %a" d_exp e diff --git a/tests/regression/56-witness/10-apron-unassume-interval.c b/tests/regression/56-witness/10-apron-unassume-interval.c new file mode 100644 index 0000000000..895f6e8533 --- /dev/null +++ b/tests/regression/56-witness/10-apron-unassume-interval.c @@ -0,0 +1,11 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 10-apron-unassume-interval.yml +#include + +int main() { + int i = 0; + while (i < 100) { + i++; + } + assert(i == 100); + return 0; +} diff --git a/tests/regression/56-witness/10-apron-unassume-interval.yml b/tests/regression/56-witness/10-apron-unassume-interval.yml new file mode 100644 index 0000000000..f7958922ae --- /dev/null +++ b/tests/regression/56-witness/10-apron-unassume-interval.yml @@ -0,0 +1,56 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 0a72f7b3-7826-4f68-bc7b-25425e95946e + creation_time: 2022-07-26T09:11:03Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g48503c690-dirty + command_line: '''./goblint'' ''--enable'' ''dbg.debug'' ''--enable'' ''dbg.regression'' + ''--html'' ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''10-apron-unassume-interval.c''' + task: + input_files: + - 10-apron-unassume-interval.c + input_file_hashes: + 10-apron-unassume-interval.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + location: + file_name: 10-apron-unassume-interval.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 6 + column: 2 + function: main + loop_invariant: + string: 100LL - (long long )i >= 0LL + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 4e078bcd-9e55-4874-a86e-0563927704a5 + creation_time: 2022-07-26T09:11:03Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g48503c690-dirty + command_line: '''./goblint'' ''--enable'' ''dbg.debug'' ''--enable'' ''dbg.regression'' + ''--html'' ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''10-apron-unassume-interval.c''' + task: + input_files: + - 10-apron-unassume-interval.c + input_file_hashes: + 10-apron-unassume-interval.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + location: + file_name: 10-apron-unassume-interval.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 6 + column: 2 + function: main + loop_invariant: + string: (long long )i >= 0LL + type: assertion + format: C From 99d51b4ff3074f09ba1869a132fbe641a3cab446 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 12:19:52 +0300 Subject: [PATCH 006/132] Clean up unassume in Apron --- src/analyses/apron/apronAnalysis.apron.ml | 49 +++++------------------ 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 2120527624..63af1258c1 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -543,47 +543,18 @@ struct | Events.Escape escaped -> Priv.escape ctx.node (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st escaped | Events.Unassume e -> - let apr = - (* let fd = Node.find_fundec ctx.node in *) - (* ignore (Pretty.printf "@@82 %a: %a\n" d_exp e D.pretty ctx.local); *) - let a = AD.bot () in - (* let ax = Var.of_string "i#1623" in *) - (* let a = AD.add_vars a [ax] in *) - let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal in - let a = AD.add_vars a (vars |> List.map V.local) in - let a = List.fold_left assert_type_bounds a vars in (* type bounds to avoid overflow in top state *) - (* let x = ApronDomain.V.to_cil_varinfo fd ax |> Option.get in *) - (* let a = AD.assert_inv a Cil.(BinOp (LAnd, BinOp (Ge, Lval (var x), integer 0, intType), BinOp (Le, Lval (var x), integer 100, intType), intType)) false in *) - let a = AD.assert_inv a e false in - (* ignore (Pretty.printf "%a\n" AD.pretty a); *) - let a' = AD.join ctx.local.apr a in - (* ignore (Pretty.printf "%a\n" AD.pretty a'); *) - a' - in - {ctx.local with apr} + let apr = AD.bot () in (* empty env *) + (* add only relevant vars to env *) + let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal in + let apr = AD.add_vars apr (List.map V.local vars) in + let apr = List.fold_left assert_type_bounds apr vars in (* add type bounds to avoid overflow in top state *) + let apr = AD.assert_inv apr e false in + let apr' = AD.join ctx.local.apr apr in + {ctx.local with apr = apr'} | _ -> st let sync ctx reason = - (* let apr = match ctx.node with - | Statement {sid=82; _} -> - let fd = Node.find_fundec ctx.node in - ignore (Pretty.printf "@@82: %a\n" D.pretty ctx.local); - let a = AD.bot () in - let ax = Var.of_string "i#1623" in - let a = AD.add_vars a [ax] in - let x = ApronDomain.V.to_cil_varinfo fd ax |> Option.get in - let a = AD.assert_inv a Cil.(BinOp (LAnd, BinOp (Ge, Lval (var x), integer 0, intType), BinOp (Lt, Lval (var x), integer 100, intType), intType)) false in - ignore (Pretty.printf "%a\n" AD.pretty a); - let a' = AD.join ctx.local.apr a in - ignore (Pretty.printf "%a\n" AD.pretty a'); - a' - | _ -> - ctx.local.apr - in *) - let apr = ctx.local.apr in - (* ctx.emit Unassume; *) - (* After the solver is finished, store the results (for later comparison) *) if !GU.postsolving then begin let keep_local = GobConfig.get_bool "ana.apron.invariant.local" in @@ -595,13 +566,13 @@ struct | Some Local -> keep_local | _ -> false in - let st = keep_filter apr var_filter in + let st = keep_filter ctx.local.apr var_filter in let old_value = RH.find_default results ctx.node (AD.bot ()) in let new_value = AD.join old_value st in RH.replace results ctx.node new_value; end; - Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg {ctx.local with apr} (reason :> [`Normal | `Join | `Return | `Init | `Thread]) + Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `Return | `Init | `Thread]) let init marshal = Priv.init () From f0f5d4d63546ef85ce6dea8f5fb5e3a60835c6ad Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 12:25:25 +0300 Subject: [PATCH 007/132] Show warnings from Spec init --- src/framework/control.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index a65cc801e5..7daa1d2e67 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -322,8 +322,12 @@ struct else None in + + (* Some happen in init, so enable this temporarily (if required by option). *) + Goblintutil.should_warn := PostSolverArg.should_warn; Spec.init marshal; Access.init file; + Goblintutil.should_warn := false; let test_domain (module D: Lattice.S): unit = let module DP = DomainProperties.All (D) in @@ -530,7 +534,7 @@ struct compare_with (Selector.choose_solver (get_string "comparesolver")) ); - (* Most warnings happen before durin postsolver, but some happen later (e.g. in finalize), so enable this for the rest (if required by option). *) + (* Most warnings happen before during postsolver, but some happen later (e.g. in finalize), so enable this for the rest (if required by option). *) Goblintutil.should_warn := PostSolverArg.should_warn; let insrt k _ s = match k with From f6e6dd2fd7c642e453a2652d44170a3c89b6ea86 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 12:30:38 +0300 Subject: [PATCH 008/132] Forbid globals in Apron unassume --- src/analyses/apron/apronAnalysis.apron.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 63af1258c1..9af4d756a4 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -546,6 +546,7 @@ struct let apr = AD.bot () in (* empty env *) (* add only relevant vars to env *) let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal in + assert (List.for_all (fun v -> not v.vglob) vars); let apr = AD.add_vars apr (List.map V.local vars) in let apr = List.fold_left assert_type_bounds apr vars in (* add type bounds to avoid overflow in top state *) let apr = AD.assert_inv apr e false in From 5d1619808f754c32ea0b0ba784e26e7d0412f619 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 13:12:57 +0300 Subject: [PATCH 009/132] Unassume witness invariants in base analysis --- src/analyses/base.ml | 42 +++++++++++++- .../56-witness/11-base-unassume-interval.c | 14 +++++ .../56-witness/11-base-unassume-interval.yml | 56 +++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 tests/regression/56-witness/11-base-unassume-interval.c create mode 100644 tests/regression/56-witness/11-base-unassume-interval.yml diff --git a/src/analyses/base.ml b/src/analyses/base.ml index ebf55d14a8..b3908bbaea 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1773,6 +1773,11 @@ struct if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; (* Be careful: inv_exp performs a meet on both arguments of the BOr / BXor. *) a, b + | LAnd -> + if ID.to_bool c = Some true then + meet_bin c c + else + a, b | op -> if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; a, b @@ -1805,7 +1810,7 @@ struct | `Int a, `Int b -> let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) let a', b' = inv_bin_int (a, b) ikind c op in - if M.tracing then M.tracel "inv" "binop: %a, a': %a, b': %a\n" d_exp e ID.pretty a' ID.pretty b'; + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty c ID.pretty a' ID.pretty b'; let st' = inv_exp a' e1 st in let st'' = inv_exp b' e2 st' in st'' @@ -2569,6 +2574,39 @@ struct (* D.join ctx.local @@ *) ctx.local + let unassume (ctx: (D.t, _, _, _) ctx) e = + let e_cpa = CPA.bot () in + let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal in + assert (List.for_all (fun v -> not v.vglob) vars); + let e_cpa = List.fold_left (fun e_cpa v -> + CPA.add v (VD.top_value v.vtype) e_cpa + ) e_cpa vars + in + (* TODO: structural unassume instead of invariant hack *) + let e_d = + (* The usual recursion trick for ctx. *) + (* Must change ctx used by ask to also use new st (not ctx.local), otherwise recursive EvalInt queries use outdated state. *) + (* Note: query is just called on base, but not any other analyses. Potentially imprecise, but seems to be sufficient for now. *) + let local: D.t = {ctx.local with cpa = e_cpa} in + let rec ctx' asked = + { ctx with + ask = (fun (type a) (q: a Queries.t) -> query' asked q) + ; local + } + and query': type a. Queries.Set.t -> a Queries.t -> a Queries.result = fun asked q -> + let anyq = Queries.Any q in + if Queries.Set.mem anyq asked then + Queries.Result.top q (* query cycle *) + else ( + let asked' = Queries.Set.add anyq asked in + query (ctx' asked') q + ) + in + let ctx = ctx' Queries.Set.empty in + invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true + in + D.join ctx.local e_d + let event ctx e octx = let st: store = ctx.local in match e with @@ -2586,6 +2624,8 @@ struct | Events.AssignSpawnedThread (lval, tid) -> (* TODO: is this type right? *) set ~ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local (eval_lv (Analyses.ask_of_ctx ctx) ctx.global ctx.local lval) (Cilfacade.typeOfLval lval) (`Thread (ValueDomain.Threads.singleton tid)) + | Events.Unassume e -> + unassume ctx e | _ -> ctx.local end diff --git a/tests/regression/56-witness/11-base-unassume-interval.c b/tests/regression/56-witness/11-base-unassume-interval.c new file mode 100644 index 0000000000..bfecee5bbe --- /dev/null +++ b/tests/regression/56-witness/11-base-unassume-interval.c @@ -0,0 +1,14 @@ +// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 11-base-unassume-interval.yml +#include + +int main() { + int i = 0; + while (i < 100) { + i++; + } + assert(i == 100); + return 0; +} + +// without unassuming: vars = 13 evals = 14 +// with unassuming: vars = 13 evals = 8 diff --git a/tests/regression/56-witness/11-base-unassume-interval.yml b/tests/regression/56-witness/11-base-unassume-interval.yml new file mode 100644 index 0000000000..df939b7ca4 --- /dev/null +++ b/tests/regression/56-witness/11-base-unassume-interval.yml @@ -0,0 +1,56 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 0a72f7b3-7826-4f68-bc7b-25425e95946e + creation_time: 2022-07-26T09:11:03Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g48503c690-dirty + command_line: '''./goblint'' ''--enable'' ''dbg.debug'' ''--enable'' ''dbg.regression'' + ''--html'' ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''11-base-unassume-interval.c''' + task: + input_files: + - 11-base-unassume-interval.c + input_file_hashes: + 11-base-unassume-interval.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + location: + file_name: 11-base-unassume-interval.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 6 + column: 2 + function: main + loop_invariant: + string: 100LL - (long long )i >= 0LL + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 4e078bcd-9e55-4874-a86e-0563927704a5 + creation_time: 2022-07-26T09:11:03Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g48503c690-dirty + command_line: '''./goblint'' ''--enable'' ''dbg.debug'' ''--enable'' ''dbg.regression'' + ''--html'' ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''11-base-unassume-interval.c''' + task: + input_files: + - 11-base-unassume-interval.c + input_file_hashes: + 11-base-unassume-interval.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + location: + file_name: 11-base-unassume-interval.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 6 + column: 2 + function: main + loop_invariant: + string: (long long )i >= 0LL + type: assertion + format: C From b53f9e6f509b2087ecbef41f8fe85f17d1bf7b37 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 16:14:54 +0300 Subject: [PATCH 010/132] Consider static in scope check --- src/domains/invariantCil.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domains/invariantCil.ml b/src/domains/invariantCil.ml index ed9e7d6ddd..e8c173f227 100644 --- a/src/domains/invariantCil.ml +++ b/src/domains/invariantCil.ml @@ -18,7 +18,7 @@ let exp_replace_original_name e = let var_is_in_scope scope vi = match Cilfacade.find_scope_fundec vi with - | None -> true + | None -> vi.vstorage <> Static (* CIL pulls static locals into globals, but they aren't syntactically in global scope *) | Some fd -> CilType.Fundec.equal fd scope class exp_is_in_scope_visitor (scope: fundec) (acc: bool ref) = object From 752f88a88e152598798e62dcd29d094a0c37980f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 16:25:57 +0300 Subject: [PATCH 011/132] Add witness unassume testing to update_suite.rb --- scripts/update_suite.rb | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 704535998e..b899f127a2 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -62,10 +62,11 @@ def clearline $dump = ARGV.last == "-d" && ARGV.pop sequential = ARGV.last == "-s" && ARGV.pop marshal = ARGV.last == "-m" && ARGV.pop +witness = ARGV.last == "-w" && ARGV.pop incremental = ARGV.last == "-i" && ARGV.pop report = ARGV.last == "-r" && ARGV.pop only = ARGV[0] unless ARGV[0].nil? -if marshal || incremental then +if marshal || witness || incremental then sequential = true end if marshal && incremental then @@ -481,6 +482,23 @@ def run () end end +class ProjectWitness < Project + def create_test_set(lines) + super(lines) + @testset.p = self + end + def run () + filename = File.basename(@path) + cmd1 = "#{$goblint} #{filename} #{@params} #{ENV['gobopt']} 1>#{@testset.warnfile} --enable dbg.debug --set printstats true --enable witness.yaml.enabled --set goblint-dir .goblint-#{@id.sub('/','-')}-witness1 2>#{@testset.statsfile}" + cmd2 = "#{$goblint} #{filename} #{@params} #{ENV['gobopt']} 1>#{@testset.warnfile}2 --set ana.activated[+] unassume --enable dbg.debug --set printstats true --set witness.yaml.unassume witness.yml --set goblint-dir .goblint-#{@id.sub('/','-')}-witness2 2>#{@testset.statsfile}2" + starttime = Time.now + run_testset(@testset, cmd1, starttime) + starttime = Time.now + run_testset(@testset, cmd2, starttime) + FileUtils.rm('witness.yml') + end +end + #processing the file information projects = [] project_ids = Set.new @@ -528,6 +546,8 @@ def run () ProjectIncr.new(id, testname, groupname, path, params, patch_path, conf_path) elsif marshal then ProjectMarshal.new(id, testname, groupname, path, params) + elsif witness then + ProjectWitness.new(id, testname, groupname, path, params) else Project.new(id, testname, groupname, path, params) end From 94dadc29f3ef4ca8845e8006658e4a1a8b0d0246 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 16:35:23 +0300 Subject: [PATCH 012/132] Ignore unassumes with globals, exclude untracked in Apron --- src/analyses/apron/apronAnalysis.apron.ml | 17 ++++--- src/analyses/base.ml | 59 ++++++++++++----------- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 9af4d756a4..8280ca30a0 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -545,13 +545,16 @@ struct | Events.Unassume e -> let apr = AD.bot () in (* empty env *) (* add only relevant vars to env *) - let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal in - assert (List.for_all (fun v -> not v.vglob) vars); - let apr = AD.add_vars apr (List.map V.local vars) in - let apr = List.fold_left assert_type_bounds apr vars in (* add type bounds to avoid overflow in top state *) - let apr = AD.assert_inv apr e false in - let apr' = AD.join ctx.local.apr apr in - {ctx.local with apr = apr'} + let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal |> List.filter AD.varinfo_tracked in + if List.for_all (fun v -> not v.vglob) vars then ( + let apr = AD.add_vars apr (List.map V.local vars) in + let apr = List.fold_left assert_type_bounds apr vars in (* add type bounds to avoid overflow in top state *) + let apr = AD.assert_inv apr e false in + let apr' = AD.join ctx.local.apr apr in + {ctx.local with apr = apr'} + ) + else + ctx.local (* TODO: support unassume with globals *) | _ -> st diff --git a/src/analyses/base.ml b/src/analyses/base.ml index b3908bbaea..7955efb0e3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2577,35 +2577,38 @@ struct let unassume (ctx: (D.t, _, _, _) ctx) e = let e_cpa = CPA.bot () in let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal in - assert (List.for_all (fun v -> not v.vglob) vars); - let e_cpa = List.fold_left (fun e_cpa v -> - CPA.add v (VD.top_value v.vtype) e_cpa - ) e_cpa vars - in - (* TODO: structural unassume instead of invariant hack *) - let e_d = - (* The usual recursion trick for ctx. *) - (* Must change ctx used by ask to also use new st (not ctx.local), otherwise recursive EvalInt queries use outdated state. *) - (* Note: query is just called on base, but not any other analyses. Potentially imprecise, but seems to be sufficient for now. *) - let local: D.t = {ctx.local with cpa = e_cpa} in - let rec ctx' asked = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> query' asked q) - ; local - } - and query': type a. Queries.Set.t -> a Queries.t -> a Queries.result = fun asked q -> - let anyq = Queries.Any q in - if Queries.Set.mem anyq asked then - Queries.Result.top q (* query cycle *) - else ( - let asked' = Queries.Set.add anyq asked in - query (ctx' asked') q - ) + if List.for_all (fun v -> not v.vglob) vars then ( + let e_cpa = List.fold_left (fun e_cpa v -> + CPA.add v (VD.top_value v.vtype) e_cpa + ) e_cpa vars in - let ctx = ctx' Queries.Set.empty in - invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true - in - D.join ctx.local e_d + (* TODO: structural unassume instead of invariant hack *) + let e_d = + (* The usual recursion trick for ctx. *) + (* Must change ctx used by ask to also use new st (not ctx.local), otherwise recursive EvalInt queries use outdated state. *) + (* Note: query is just called on base, but not any other analyses. Potentially imprecise, but seems to be sufficient for now. *) + let local: D.t = {ctx.local with cpa = e_cpa} in + let rec ctx' asked = + { ctx with + ask = (fun (type a) (q: a Queries.t) -> query' asked q) + ; local + } + and query': type a. Queries.Set.t -> a Queries.t -> a Queries.result = fun asked q -> + let anyq = Queries.Any q in + if Queries.Set.mem anyq asked then + Queries.Result.top q (* query cycle *) + else ( + let asked' = Queries.Set.add anyq asked in + query (ctx' asked') q + ) + in + let ctx = ctx' Queries.Set.empty in + invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true + in + D.join ctx.local e_d + ) + else + ctx.local (* TODO: support unassume with globals *) let event ctx e octx = let st: store = ctx.local in From b5cb10ec5226cd401297239b51631ad2c87152eb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 17:37:26 +0300 Subject: [PATCH 013/132] Remove witness.yml files from update_suite.rb --- scripts/update_suite.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index b899f127a2..9b0d23b6c4 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -495,7 +495,7 @@ def run () run_testset(@testset, cmd1, starttime) starttime = Time.now run_testset(@testset, cmd2, starttime) - FileUtils.rm('witness.yml') + FileUtils.rm_f('witness.yml') end end From 10c28a050fc51fcf540f70872e584c76f97b26bf Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 26 Jul 2022 17:38:30 +0300 Subject: [PATCH 014/132] Add info about unassuming --- src/analyses/apron/apronAnalysis.apron.ml | 5 ++++- src/analyses/base.ml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 8280ca30a0..933308603c 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -551,10 +551,13 @@ struct let apr = List.fold_left assert_type_bounds apr vars in (* add type bounds to avoid overflow in top state *) let apr = AD.assert_inv apr e false in let apr' = AD.join ctx.local.apr apr in + M.info ~category:Witness "apron unassumed invariant: %a" d_exp e; {ctx.local with apr = apr'} ) - else + else ( + M.info ~category:Witness "apron didn't unassume invariant: %a" d_exp e; ctx.local (* TODO: support unassume with globals *) + ) | _ -> st diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7955efb0e3..0f40614096 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2605,10 +2605,13 @@ struct let ctx = ctx' Queries.Set.empty in invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true in + M.info ~category:Witness "base unassumed invariant: %a" d_exp e; D.join ctx.local e_d ) - else + else ( + M.info ~category:Witness "base didn't unassume invariant: %a" d_exp e; ctx.local (* TODO: support unassume with globals *) + ) let event ctx e octx = let st: store = ctx.local in From 4c8a7374d47c3e9b1129c1694f17039035631e9d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Jul 2022 12:27:40 +0300 Subject: [PATCH 015/132] Emit Unassume from other transfer functions --- src/analyses/unassumeAnalysis.ml | 26 ++++++++- .../56-witness/12-apron-unassume-branch.c | 11 ++++ .../56-witness/12-apron-unassume-branch.yml | 56 +++++++++++++++++++ .../56-witness/13-base-unassume-branch.c | 14 +++++ .../56-witness/13-base-unassume-branch.yml | 56 +++++++++++++++++++ 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 tests/regression/56-witness/12-apron-unassume-branch.c create mode 100644 tests/regression/56-witness/12-apron-unassume-branch.yml create mode 100644 tests/regression/56-witness/13-base-unassume-branch.c create mode 100644 tests/regression/56-witness/13-base-unassume-branch.yml diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 4e315ecc82..b4c36f2a53 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -104,13 +104,37 @@ struct | Error (`Msg e) -> M.info_noloc ~category:Witness "couldn't parse entry: %s" e ) yaml_entries - let assign ctx lv e = + let emit_unassume ctx = match NH.find_all invs ctx.node with | x :: xs -> let e = List.fold_left (fun a b -> Cil.(BinOp (LAnd, a, b, intType))) x xs in ctx.emit (Unassume e) | [] -> () + + let assign ctx lv e = + emit_unassume ctx + + let branch ctx e tv = + emit_unassume ctx + + let body ctx fd = + emit_unassume ctx + + let asm ctx = + emit_unassume ctx + + let skip ctx = + emit_unassume ctx + + let special ctx lv f args = + emit_unassume ctx + + let combine ctx lv fe f args fc fd = + emit_unassume ctx + + (* not in sync, query, entry, threadenter because they aren't final transfer function on edge *) + (* not in vdecl, return, threadspawn because unnecessary targets for invariants? *) end let _ = diff --git a/tests/regression/56-witness/12-apron-unassume-branch.c b/tests/regression/56-witness/12-apron-unassume-branch.c new file mode 100644 index 0000000000..99edbcecb2 --- /dev/null +++ b/tests/regression/56-witness/12-apron-unassume-branch.c @@ -0,0 +1,11 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 12-apron-unassume-branch.yml +#include + +int main() { + int i = 0; + while (i < 100) { + i++; + } + assert(i == 100); // TODO: avoid widening when unassume inside loop + return 0; +} diff --git a/tests/regression/56-witness/12-apron-unassume-branch.yml b/tests/regression/56-witness/12-apron-unassume-branch.yml new file mode 100644 index 0000000000..2381291880 --- /dev/null +++ b/tests/regression/56-witness/12-apron-unassume-branch.yml @@ -0,0 +1,56 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 0a72f7b3-7826-4f68-bc7b-25425e95946e + creation_time: 2022-07-26T09:11:03Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g48503c690-dirty + command_line: '''./goblint'' ''--enable'' ''dbg.debug'' ''--enable'' ''dbg.regression'' + ''--html'' ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''12-apron-unassume-branch.c''' + task: + input_files: + - 12-apron-unassume-branch.c + input_file_hashes: + 12-apron-unassume-branch.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + location: + file_name: 12-apron-unassume-branch.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 7 + column: 4 + function: main + loop_invariant: + string: 99LL - (long long )i >= 0LL + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 4e078bcd-9e55-4874-a86e-0563927704a5 + creation_time: 2022-07-26T09:11:03Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g48503c690-dirty + command_line: '''./goblint'' ''--enable'' ''dbg.debug'' ''--enable'' ''dbg.regression'' + ''--html'' ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''12-apron-unassume-branch.c''' + task: + input_files: + - 12-apron-unassume-branch.c + input_file_hashes: + 12-apron-unassume-branch.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + location: + file_name: 12-apron-unassume-branch.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 7 + column: 4 + function: main + loop_invariant: + string: (long long )i >= 0LL + type: assertion + format: C diff --git a/tests/regression/56-witness/13-base-unassume-branch.c b/tests/regression/56-witness/13-base-unassume-branch.c new file mode 100644 index 0000000000..d03dc939b3 --- /dev/null +++ b/tests/regression/56-witness/13-base-unassume-branch.c @@ -0,0 +1,14 @@ +// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 13-base-unassume-branch.yml +#include + +int main() { + int i = 0; + while (i < 100) { + i++; + } + assert(i == 100); + return 0; +} + +// without unassuming: vars = 13 evals = 14 +// with unassuming: vars = 13 evals = 12 diff --git a/tests/regression/56-witness/13-base-unassume-branch.yml b/tests/regression/56-witness/13-base-unassume-branch.yml new file mode 100644 index 0000000000..22253938b7 --- /dev/null +++ b/tests/regression/56-witness/13-base-unassume-branch.yml @@ -0,0 +1,56 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 0a72f7b3-7826-4f68-bc7b-25425e95946e + creation_time: 2022-07-26T09:11:03Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g48503c690-dirty + command_line: '''./goblint'' ''--enable'' ''dbg.debug'' ''--enable'' ''dbg.regression'' + ''--html'' ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''13-base-unassume-branch.c''' + task: + input_files: + - 13-base-unassume-branch.c + input_file_hashes: + 13-base-unassume-branch.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + location: + file_name: 13-base-unassume-branch.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 7 + column: 4 + function: main + loop_invariant: + string: 99LL - (long long )i >= 0LL + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 4e078bcd-9e55-4874-a86e-0563927704a5 + creation_time: 2022-07-26T09:11:03Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g48503c690-dirty + command_line: '''./goblint'' ''--enable'' ''dbg.debug'' ''--enable'' ''dbg.regression'' + ''--html'' ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''13-base-unassume-branch.c''' + task: + input_files: + - 13-base-unassume-branch.c + input_file_hashes: + 13-base-unassume-branch.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + location: + file_name: 13-base-unassume-branch.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 7 + column: 4 + function: main + loop_invariant: + string: (long long )i >= 0LL + type: assertion + format: C From e674f4f3cc2d71980bc96c699344b57522943fbe Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Jul 2022 13:17:12 +0300 Subject: [PATCH 016/132] Add preconditioned invariant unassuming --- src/analyses/unassumeAnalysis.ml | 107 ++++++++++++++++-- .../14-base-unassume-precondition.c | 19 ++++ .../14-base-unassume-precondition.yml | 89 +++++++++++++++ 3 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 tests/regression/56-witness/14-base-unassume-precondition.c create mode 100644 tests/regression/56-witness/14-base-unassume-precondition.yml diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index b4c36f2a53..2ec61dee20 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -2,18 +2,32 @@ open Analyses module NH = CfgTools.NH +module FH = Hashtbl.Make (CilType.Fundec) module Spec = struct - include UnitAnalysis.Spec + include Analyses.IdentitySpec let name () = "unassume" + module C = Printable.Unit + module D = SetDomain.Make (CilType.Exp) + + let startstate _ = D.empty () + let morphstate _ _ = D.empty () + let exitstate _ = D.empty () + + let context _ _ = () + let should_join _ _ = false + module Locator = WitnessUtil.Locator (Node) let locator: Locator.t ref = ref (Locator.create ()) (* empty default, so don't have to use option everywhere *) let invs: Cil.exp NH.t = NH.create 100 + let fun_pres: Cil.exp FH.t = FH.create 100 + let pre_invs: (Cil.exp * Cil.exp) NH.t = NH.create 100 + let init _ = locator := Locator.create (); (* TODO: add Locator.clear *) let module Cfg = (val !MyCFG.current_cfg) in @@ -56,6 +70,8 @@ struct let inv_parser = InvariantParser.create !Cilfacade.current_file in NH.clear invs; + FH.clear fun_pres; + NH.clear pre_invs; let unassume_entry (entry: YamlWitnessType.Entry.t) = @@ -91,9 +107,58 @@ struct M.warn ~category:Witness ~loc "couldn't locate invariant: %s" inv in + let unassume_precondition_nodes_invariant ~loc ~nodes pre inv = + match InvariantParser.parse_cabs pre, InvariantParser.parse_cabs inv with + | Ok pre_cabs, Ok inv_cabs -> + + Locator.ES.iter (fun n -> + let fundec = Node.find_fundec n in + + match InvariantParser.parse_cil inv_parser ~fundec ~loc pre_cabs with + | Ok pre_exp -> + M.debug ~category:Witness ~loc "located precondition to %a: %a" CilType.Fundec.pretty fundec Cil.d_exp pre_exp; + FH.add fun_pres fundec pre_exp; + + begin match InvariantParser.parse_cil inv_parser ~fundec ~loc inv_cabs with + | Ok inv_exp -> + M.debug ~category:Witness ~loc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; + NH.add pre_invs n (pre_exp, inv_exp) + | Error e -> + M.error ~category:Witness ~loc "CIL couldn't parse invariant: %s" inv; + M.info ~category:Witness ~loc "invariant has undefined variables or side effects: %s" inv + end + + | Error e -> + M.error ~category:Witness ~loc "CIL couldn't parse precondition: %s" pre; + M.info ~category:Witness ~loc "precondition has undefined variables or side effects: %s" pre + ) nodes; + + | Error e, _ -> + M.error ~category:Witness ~loc "Frontc couldn't parse precondition: %s" pre; + M.info ~category:Witness ~loc "precondition has invalid syntax: %s" pre + + | _, Error e -> + M.error ~category:Witness ~loc "Frontc couldn't parse invariant: %s" inv; + M.info ~category:Witness ~loc "invariant has invalid syntax: %s" inv + in + + let unassume_precondition_loop_invariant (precondition_loop_invariant: YamlWitnessType.PreconditionLoopInvariant.t) = + let loc = loc_of_location precondition_loop_invariant.location in + let pre = precondition_loop_invariant.precondition.string in + let inv = precondition_loop_invariant.loop_invariant.string in + + match Locator.find_opt !locator loc with + | Some nodes -> + unassume_precondition_nodes_invariant ~loc ~nodes pre inv + | None -> + M.warn ~category:Witness ~loc "couldn't locate invariant: %s" inv + in + match entry.entry_type with | LoopInvariant x -> unassume_loop_invariant x + | PreconditionLoopInvariant x -> + unassume_precondition_loop_invariant x | entry_type -> M.info_noloc ~category:Witness "cannot unassume entry of type %s" (YamlWitnessType.EntryType.entry_type entry_type) in @@ -105,12 +170,24 @@ struct ) yaml_entries let emit_unassume ctx = - match NH.find_all invs ctx.node with - | x :: xs -> - let e = List.fold_left (fun a b -> Cil.(BinOp (LAnd, a, b, intType))) x xs in - ctx.emit (Unassume e) - | [] -> - () + let es = NH.find_all invs ctx.node in + let es = + NH.find_all pre_invs ctx.node + |> List.fold_left (fun acc (pre, inv) -> + if D.mem pre ctx.local then + inv :: acc + else + acc + ) es + in + begin match es with + | x :: xs -> + let e = List.fold_left (fun a b -> Cil.(BinOp (LAnd, a, b, intType))) x xs in + ctx.emit (Unassume e) + | [] -> + () + end; + ctx.local let assign ctx lv e = emit_unassume ctx @@ -119,7 +196,18 @@ struct emit_unassume ctx let body ctx fd = - emit_unassume ctx + let pres = FH.find_all fun_pres fd in + let st = List.fold_left (fun acc pre -> + let v = ctx.ask (EvalInt pre) in + (* M.debug ~category:Witness "%a precondition %a evaluated to %a" CilType.Fundec.pretty fd CilType.Exp.pretty pre Queries.ID.pretty v; *) + if Queries.ID.to_bool v = Some true then + D.add pre acc + else + acc + ) (D.empty ()) pres + in + + emit_unassume {ctx with local = st} (* doesn't query, so no need to redefine ask *) let asm ctx = emit_unassume ctx @@ -130,6 +218,9 @@ struct let special ctx lv f args = emit_unassume ctx + let enter ctx lv f args = + [(ctx.local, D.empty ())] + let combine ctx lv fe f args fc fd = emit_unassume ctx diff --git a/tests/regression/56-witness/14-base-unassume-precondition.c b/tests/regression/56-witness/14-base-unassume-precondition.c new file mode 100644 index 0000000000..2aece436d3 --- /dev/null +++ b/tests/regression/56-witness/14-base-unassume-precondition.c @@ -0,0 +1,19 @@ +// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 14-base-unassume-precondition.yml +#include + +void foo(int n) { + int i = 0; + while (i < n) { + i++; + } + assert(i == n); +} + +int main() { + foo(50); + foo(100); + return 0; +} + +// without unassuming: vars = 25 evals = 32 +// with unassuming: vars = 25 evals = 20 diff --git a/tests/regression/56-witness/14-base-unassume-precondition.yml b/tests/regression/56-witness/14-base-unassume-precondition.yml new file mode 100644 index 0000000000..2d84e4ea9f --- /dev/null +++ b/tests/regression/56-witness/14-base-unassume-precondition.yml @@ -0,0 +1,89 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 26e6d2de-13a6-4610-9b08-ee3d9e6b9338 + creation_time: 2022-07-27T09:38:32Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g4c8a7374d-dirty + command_line: '''../../../goblint'' ''--enable'' ''dbg.debug'' ''--enable'' + ''ana.int.interval'' ''14-base-unassume-precondition.c'' ''--enable'' ''witness.yaml.enabled''' + task: + input_files: + - 14-base-unassume-precondition.c + input_file_hashes: + 14-base-unassume-precondition.c: 67e6b56700825db19b04a972291f7b05465b5ec2f797799026fae5c0d83e536f + data_model: LP64 + language: C + location: + file_name: 14-base-unassume-precondition.c + file_hash: 67e6b56700825db19b04a972291f7b05465b5ec2f797799026fae5c0d83e536f + line: 6 + column: 2 + function: foo + loop_invariant: + string: 0 <= i + type: assertion + format: C +- entry_type: precondition_loop_invariant + metadata: + format_version: "0.1" + uuid: 9511f3ea-4c5c-48a9-9df3-c69def7c53b0 + creation_time: 2022-07-27T09:38:32Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g4c8a7374d-dirty + command_line: '''../../../goblint'' ''--enable'' ''dbg.debug'' ''--enable'' + ''ana.int.interval'' ''14-base-unassume-precondition.c'' ''--enable'' ''witness.yaml.enabled''' + task: + input_files: + - 14-base-unassume-precondition.c + input_file_hashes: + 14-base-unassume-precondition.c: 67e6b56700825db19b04a972291f7b05465b5ec2f797799026fae5c0d83e536f + data_model: LP64 + language: C + location: + file_name: 14-base-unassume-precondition.c + file_hash: 67e6b56700825db19b04a972291f7b05465b5ec2f797799026fae5c0d83e536f + line: 6 + column: 2 + function: foo + loop_invariant: + string: i <= 100 + type: assertion + format: C + precondition: + string: n == 100 + type: assertion + format: C +- entry_type: precondition_loop_invariant + metadata: + format_version: "0.1" + uuid: 57e21ea2-5bc3-4adc-a703-159d9733049e + creation_time: 2022-07-27T09:38:32Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g4c8a7374d-dirty + command_line: '''../../../goblint'' ''--enable'' ''dbg.debug'' ''--enable'' + ''ana.int.interval'' ''14-base-unassume-precondition.c'' ''--enable'' ''witness.yaml.enabled''' + task: + input_files: + - 14-base-unassume-precondition.c + input_file_hashes: + 14-base-unassume-precondition.c: 67e6b56700825db19b04a972291f7b05465b5ec2f797799026fae5c0d83e536f + data_model: LP64 + language: C + location: + file_name: 14-base-unassume-precondition.c + file_hash: 67e6b56700825db19b04a972291f7b05465b5ec2f797799026fae5c0d83e536f + line: 6 + column: 2 + function: foo + loop_invariant: + string: i <= 50 + type: assertion + format: C + precondition: + string: n == 50 + type: assertion + format: C From 2b48d41e035bbdc7fc134c519e66b4bd771e22ef Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Jul 2022 13:17:37 +0300 Subject: [PATCH 017/132] Fix precondition CIL parse errors in witness validation --- src/witness/yamlWitness.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index d8e08c9f7c..c11d6fa72c 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -420,8 +420,8 @@ struct else false | Error e -> - M.error ~category:Witness ~loc "CIL couldn't parse precondition: %s" inv; - M.info ~category:Witness ~loc "precondition has undefined variables or side effects: %s" inv; + M.error ~category:Witness ~loc "CIL couldn't parse precondition: %s" pre; + M.info ~category:Witness ~loc "precondition has undefined variables or side effects: %s" pre; false in From 557e12f68e826175d8aa3745cffd4770abd12acc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Jul 2022 14:36:02 +0300 Subject: [PATCH 018/132] Replace unassume pre_invs assoc list with Hashtbl --- src/analyses/unassumeAnalysis.ml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 2ec61dee20..30a480191e 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -3,6 +3,7 @@ open Analyses module NH = CfgTools.NH module FH = Hashtbl.Make (CilType.Fundec) +module EH = Hashtbl.Make (CilType.Exp) module Spec = struct @@ -26,7 +27,7 @@ struct let invs: Cil.exp NH.t = NH.create 100 let fun_pres: Cil.exp FH.t = FH.create 100 - let pre_invs: (Cil.exp * Cil.exp) NH.t = NH.create 100 + let pre_invs: Cil.exp EH.t NH.t = NH.create 100 let init _ = locator := Locator.create (); (* TODO: add Locator.clear *) @@ -122,7 +123,9 @@ struct begin match InvariantParser.parse_cil inv_parser ~fundec ~loc inv_cabs with | Ok inv_exp -> M.debug ~category:Witness ~loc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; - NH.add pre_invs n (pre_exp, inv_exp) + if not (NH.mem pre_invs n) then + NH.replace pre_invs n (EH.create 10); + EH.add (NH.find pre_invs n) pre_exp inv_exp | Error e -> M.error ~category:Witness ~loc "CIL couldn't parse invariant: %s" inv; M.info ~category:Witness ~loc "invariant has undefined variables or side effects: %s" inv @@ -171,14 +174,11 @@ struct let emit_unassume ctx = let es = NH.find_all invs ctx.node in - let es = - NH.find_all pre_invs ctx.node - |> List.fold_left (fun acc (pre, inv) -> - if D.mem pre ctx.local then - inv :: acc - else - acc - ) es + let es = D.fold (fun pre acc -> + match NH.find_option pre_invs ctx.node with + | Some eh -> EH.find_all eh pre @ acc + | None -> acc + ) ctx.local es in begin match es with | x :: xs -> From b252258b7fad04391c58e5bbd613faf8af2eb511 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Jul 2022 15:00:51 +0300 Subject: [PATCH 019/132] Add script for checking evals count changes in regression tests with witnesses --- scripts/suite-result-witness-evals.py | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100755 scripts/suite-result-witness-evals.py diff --git a/scripts/suite-result-witness-evals.py b/scripts/suite-result-witness-evals.py new file mode 100755 index 0000000000..60b6efbc00 --- /dev/null +++ b/scripts/suite-result-witness-evals.py @@ -0,0 +1,42 @@ +#!/usr/bin/python3 + +# checks evals count changes after ./scripts/update_suite.rb -w + + +from pathlib import Path +import re + + +suite_result_path = Path("./tests/suite_result") + +def extract_evals(path): + with path.open() as f: + s = f.read() + m = re.search(r"evals = (\d+)", s) + if m is not None: + return int(m.group(1)) + else: + return None + +count_same = 0 +count_better = 0 +count_worse = 0 + +for after_path in suite_result_path.glob("*/*.warn.txt2"): + before_path = after_path.with_suffix(".txt") + print(after_path, end=" ") + before_evals = extract_evals(before_path) + after_evals = extract_evals(after_path) + print(before_evals, after_evals) + if before_evals is not None and after_evals is not None: + if before_evals == after_evals: + count_same += 1 + elif before_evals > after_evals: + count_better += 1 + else: + count_worse += 1 + print(f"WORSE! by {after_evals - before_evals}") + +print(f"same: {count_same}") +print(f"better: {count_better}") +print(f"worse: {count_worse}") From b1eb2ebad12939228422539acb78361d4d3e05b6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Jul 2022 17:26:17 +0300 Subject: [PATCH 020/132] Fix update_suite.rb -w not checking anything --- scripts/update_suite.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 9b0d23b6c4..f87e6be10c 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -489,8 +489,8 @@ def create_test_set(lines) end def run () filename = File.basename(@path) - cmd1 = "#{$goblint} #{filename} #{@params} #{ENV['gobopt']} 1>#{@testset.warnfile} --enable dbg.debug --set printstats true --enable witness.yaml.enabled --set goblint-dir .goblint-#{@id.sub('/','-')}-witness1 2>#{@testset.statsfile}" - cmd2 = "#{$goblint} #{filename} #{@params} #{ENV['gobopt']} 1>#{@testset.warnfile}2 --set ana.activated[+] unassume --enable dbg.debug --set printstats true --set witness.yaml.unassume witness.yml --set goblint-dir .goblint-#{@id.sub('/','-')}-witness2 2>#{@testset.statsfile}2" + cmd1 = "#{$goblint} #{filename} #{@params} #{ENV['gobopt']} 1>#{@testset.warnfile}0 --enable dbg.debug --set printstats true --enable witness.yaml.enabled --set goblint-dir .goblint-#{@id.sub('/','-')}-witness1 2>#{@testset.statsfile}0" + cmd2 = "#{$goblint} #{filename} #{@params} #{ENV['gobopt']} 1>#{@testset.warnfile} --set ana.activated[+] unassume --enable dbg.debug --set printstats true --set witness.yaml.unassume witness.yml --set goblint-dir .goblint-#{@id.sub('/','-')}-witness2 2>#{@testset.statsfile}" starttime = Time.now run_testset(@testset, cmd1, starttime) starttime = Time.now From 1007f57eba963dff79ad5441ad0ad9573ef6edf4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Jul 2022 17:40:51 +0300 Subject: [PATCH 021/132] Hack base unassume to not lose precision on non-first vars --- src/analyses/base.ml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 0f40614096..0708dfc60e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1834,7 +1834,9 @@ struct if is_some_bot v then raise Deadcode else ( if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v ID.pretty c VD.pretty c'; - set' (Var var,NoOffset) v st + let r = set' (Var var,NoOffset) v st in + if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; + r ) | Mem _, _ -> (* For accesses via pointers, not yet *) @@ -2599,13 +2601,20 @@ struct Queries.Result.top q (* query cycle *) else ( let asked' = Queries.Set.add anyq asked in - query (ctx' asked') q + match q with + | MayEscape _ -> false (* HACK: avoids get_var in inv_exp thinking non-first var is global *) + | _ -> + query (ctx' asked') q ) in let ctx = ctx' Queries.Set.empty in - invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true + if M.tracing then M.traceli "unassume" "base unassuming\n"; + let r = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true in + if M.tracing then M.traceu "unassume" "base unassumed\n"; + r in M.info ~category:Witness "base unassumed invariant: %a" d_exp e; + M.debug ~category:Witness "base unassumed state: %a" D.pretty e_d; D.join ctx.local e_d ) else ( From a96aa0a98b9c0e9bc8883539473a7965662ca0ca Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 8 Aug 2022 14:45:16 +0300 Subject: [PATCH 022/132] Add witness test for problematic base unassume invariant queries --- .../56-witness/15-base-unassume-query.c | 24 +++++++++ .../56-witness/15-base-unassume-query.yml | 52 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tests/regression/56-witness/15-base-unassume-query.c create mode 100644 tests/regression/56-witness/15-base-unassume-query.yml diff --git a/tests/regression/56-witness/15-base-unassume-query.c b/tests/regression/56-witness/15-base-unassume-query.c new file mode 100644 index 0000000000..63acf53954 --- /dev/null +++ b/tests/regression/56-witness/15-base-unassume-query.c @@ -0,0 +1,24 @@ +// PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 15-base-unassume-query.yml +#include +#include + +void *t_fun(void *arg) { + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); // enter multithreaded + + int i, j; + i = 2; + j = 3; + assert(i == 2); + assert(j == 3); + + // screw with ThreadEscape.is_escaped via vaddrof + int *p, *q; + p = &i; + q = &j; + return 0; +} \ No newline at end of file diff --git a/tests/regression/56-witness/15-base-unassume-query.yml b/tests/regression/56-witness/15-base-unassume-query.yml new file mode 100644 index 0000000000..46b0ffc4c5 --- /dev/null +++ b/tests/regression/56-witness/15-base-unassume-query.yml @@ -0,0 +1,52 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: dc8526c7-69d5-4ec2-b90c-670bd19c6f1a + creation_time: 2022-08-08T11:19:10Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g1007f57eb-dirty + command_line: '''./goblint'' ''--enable'' ''witness.yaml.enabled'' ''unassume.c''' + task: + input_files: + - 15-base-unassume-query.c + input_file_hashes: + unassume.c: 8bad487944a1627a53420a3ea2c079197fc5313416ed1f3e77e9d4c68f236bdd + data_model: LP64 + language: C + location: + file_name: 15-base-unassume-query.c + file_hash: 8bad487944a1627a53420a3ea2c079197fc5313416ed1f3e77e9d4c68f236bdd + line: 16 + column: 2 + function: main + loop_invariant: + string: i == 2 + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 48ab49c6-c030-446d-9b31-673979f1cbd0 + creation_time: 2022-08-08T11:19:10Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g1007f57eb-dirty + command_line: '''./goblint'' ''--enable'' ''witness.yaml.enabled'' ''unassume.c''' + task: + input_files: + - 15-base-unassume-query.c + input_file_hashes: + unassume.c: 8bad487944a1627a53420a3ea2c079197fc5313416ed1f3e77e9d4c68f236bdd + data_model: LP64 + language: C + location: + file_name: 15-base-unassume-query.c + file_hash: 8bad487944a1627a53420a3ea2c079197fc5313416ed1f3e77e9d4c68f236bdd + line: 16 + column: 2 + function: main + loop_invariant: + string: j == 3 + type: assertion + format: C From 048318bd52b96892b7ce98483303c618d028a8c8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 8 Aug 2022 14:45:44 +0300 Subject: [PATCH 023/132] Change base unassume invariant to delegate safe queries outside --- src/analyses/base.ml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 0708dfc60e..26ad9bf3d4 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2602,8 +2602,31 @@ struct else ( let asked' = Queries.Set.add anyq asked in match q with - | MayEscape _ -> false (* HACK: avoids get_var in inv_exp thinking non-first var is global *) + | MayEscape _ + | MayBePublic _ + | MayBePublicWithout _ + | MustBeProtectedBy _ + | MustLockset + | MustBeAtomic + | MustBeSingleThreaded + | MustBeUniqueThread + | CurrentThreadId + | MayBeThreadReturn + | PartAccess _ + | IsHeapVar _ + | IsMultiple _ + | CreatedThreads + | MustJoinedThreads -> + (* These queries are safe to ask from outside, + where base doesn't have the partial top local state. + They are also needed for sensible eval behavior via [inv_exp] + such that everything wouldn't be may escaped. *) + ctx.ask q | _ -> + (* Other queries are not safe, because they would + query the local value state instead of top. + Therefore, these are answered only by base on the + partial top local state. *) query (ctx' asked') q ) in From e4b5d9f120e15945d01dc38024a605cf8ac38c5e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 22 Aug 2022 14:58:42 +0300 Subject: [PATCH 024/132] Make logical operator invariant result more precise --- 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 0434a3378b..dc2fc70ac5 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2080,8 +2080,10 @@ struct in if eval_bool exp st = Some (not tv) then raise Deadcode (* we already know that the branch is dead *) else + (* C11 6.5.13, 6.5.14, 6.5.3.3: LAnd, LOr and LNot also return 0 or 1 *) let is_cmp = function - | BinOp ((Lt | Gt | Le | Ge | Eq | Ne), _, _, t) -> true + | UnOp (LNot, _, _) + | BinOp ((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr), _, _, _) -> true | _ -> false in try From 82e0e31ba9d1b94032c455ab5bc9ffbe93d353e3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 30 Aug 2022 18:07:41 +0300 Subject: [PATCH 025/132] Add some TODO base unassume tests --- .../56-witness/16-base-unassume-dependent.c | 15 ++++ .../56-witness/16-base-unassume-dependent.yml | 84 +++++++++++++++++++ .../56-witness/17-base-unassume-tauto.c | 9 ++ .../56-witness/17-base-unassume-tauto.yml | 28 +++++++ .../56-witness/18-base-unassume-contra.c | 9 ++ .../56-witness/18-base-unassume-contra.yml | 28 +++++++ 6 files changed, 173 insertions(+) create mode 100644 tests/regression/56-witness/16-base-unassume-dependent.c create mode 100644 tests/regression/56-witness/16-base-unassume-dependent.yml create mode 100644 tests/regression/56-witness/17-base-unassume-tauto.c create mode 100644 tests/regression/56-witness/17-base-unassume-tauto.yml create mode 100644 tests/regression/56-witness/18-base-unassume-contra.c create mode 100644 tests/regression/56-witness/18-base-unassume-contra.yml diff --git a/tests/regression/56-witness/16-base-unassume-dependent.c b/tests/regression/56-witness/16-base-unassume-dependent.c new file mode 100644 index 0000000000..42d1b08742 --- /dev/null +++ b/tests/regression/56-witness/16-base-unassume-dependent.c @@ -0,0 +1,15 @@ +// SKIP PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 16-base-unassume-dependent.yml --enable ana.int.interval +#include + +int main() { + int i, j; + i = 0; + j = 42; + __goblint_check(i == 0); // TODO UNKNOWN (intentional by unassume) + __goblint_check(j == 42); // TODO UNKNOWN (intentional by unassume) + __goblint_check(0 <= i); + __goblint_check(i <= 42); + __goblint_check(0 <= j); + __goblint_check(j <= 42); + return 0; +} \ No newline at end of file diff --git a/tests/regression/56-witness/16-base-unassume-dependent.yml b/tests/regression/56-witness/16-base-unassume-dependent.yml new file mode 100644 index 0000000000..78c4ce4490 --- /dev/null +++ b/tests/regression/56-witness/16-base-unassume-dependent.yml @@ -0,0 +1,84 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 349896cf-62cc-42e7-b9a3-f5f422ff4071 + creation_time: 2022-08-30T14:46:04Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-ge4b5d9f12-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''16-base-unassume-dependent.c'' + ''--enable'' ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--set'' + ''printstats'' ''true'' ''--set'' ''goblint-dir'' ''.goblint-56-16''' + task: + input_files: + - 16-base-unassume-dependent.c + input_file_hashes: + 16-base-unassume-dependent.c: 82334866168bcd385cfdd9f7e9b4431c30259b8cbae87905462829efa85de98c + data_model: LP64 + language: C + location: + file_name: 16-base-unassume-dependent.c + file_hash: 82334866168bcd385cfdd9f7e9b4431c30259b8cbae87905462829efa85de98c + line: 8 + column: 2 + function: main + loop_invariant: + string: 0 <= i + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 349896cf-62cc-42e7-b9a3-f5f422ff4071 + creation_time: 2022-08-30T14:46:04Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-ge4b5d9f12-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''16-base-unassume-dependent.c'' + ''--enable'' ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--set'' + ''printstats'' ''true'' ''--set'' ''goblint-dir'' ''.goblint-56-16''' + task: + input_files: + - 16-base-unassume-dependent.c + input_file_hashes: + 16-base-unassume-dependent.c: 82334866168bcd385cfdd9f7e9b4431c30259b8cbae87905462829efa85de98c + data_model: LP64 + language: C + location: + file_name: 16-base-unassume-dependent.c + file_hash: 82334866168bcd385cfdd9f7e9b4431c30259b8cbae87905462829efa85de98c + line: 8 + column: 2 + function: main + loop_invariant: + string: i <= j + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 349896cf-62cc-42e7-b9a3-f5f422ff4071 + creation_time: 2022-08-30T14:46:04Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-ge4b5d9f12-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''16-base-unassume-dependent.c'' + ''--enable'' ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--set'' + ''printstats'' ''true'' ''--set'' ''goblint-dir'' ''.goblint-56-16''' + task: + input_files: + - 16-base-unassume-dependent.c + input_file_hashes: + 16-base-unassume-dependent.c: 82334866168bcd385cfdd9f7e9b4431c30259b8cbae87905462829efa85de98c + data_model: LP64 + language: C + location: + file_name: 16-base-unassume-dependent.c + file_hash: 82334866168bcd385cfdd9f7e9b4431c30259b8cbae87905462829efa85de98c + line: 8 + column: 2 + function: main + loop_invariant: + string: j <= 42 + type: assertion + format: C diff --git a/tests/regression/56-witness/17-base-unassume-tauto.c b/tests/regression/56-witness/17-base-unassume-tauto.c new file mode 100644 index 0000000000..b1516b43b4 --- /dev/null +++ b/tests/regression/56-witness/17-base-unassume-tauto.c @@ -0,0 +1,9 @@ +// SKIP PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 17-base-unassume-tauto.yml --enable ana.int.interval +#include + +int main() { + int i; + i = 0; + __goblint_check(i == 0); // TODO UNKNOWN (intentional by unassume) + return 0; +} \ No newline at end of file diff --git a/tests/regression/56-witness/17-base-unassume-tauto.yml b/tests/regression/56-witness/17-base-unassume-tauto.yml new file mode 100644 index 0000000000..5f5bca4f64 --- /dev/null +++ b/tests/regression/56-witness/17-base-unassume-tauto.yml @@ -0,0 +1,28 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 828f8b0f-590b-4180-bef1-d2b18cc240e6 + creation_time: 2022-08-30T14:58:31Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-ge4b5d9f12-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''17-base-unassume-tauto.c'' + ''--enable'' ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--set'' + ''printstats'' ''true'' ''--set'' ''goblint-dir'' ''.goblint-56-17''' + task: + input_files: + - 17-base-unassume-tauto.c + input_file_hashes: + 17-base-unassume-tauto.c: a1dbab3d2dc60945ace118bcacd88e4d448ddb3aaa5e9aebe3d0872266256f89 + data_model: LP64 + language: C + location: + file_name: 17-base-unassume-tauto.c + file_hash: a1dbab3d2dc60945ace118bcacd88e4d448ddb3aaa5e9aebe3d0872266256f89 + line: 8 + column: 2 + function: main + loop_invariant: + string: i <= i + 1 + type: assertion + format: C diff --git a/tests/regression/56-witness/18-base-unassume-contra.c b/tests/regression/56-witness/18-base-unassume-contra.c new file mode 100644 index 0000000000..7703844f60 --- /dev/null +++ b/tests/regression/56-witness/18-base-unassume-contra.c @@ -0,0 +1,9 @@ +// PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 18-base-unassume-contra.yml --enable ana.int.interval +#include + +int main() { + int i; + i = 0; + __goblint_check(i == 0); + return 0; +} \ No newline at end of file diff --git a/tests/regression/56-witness/18-base-unassume-contra.yml b/tests/regression/56-witness/18-base-unassume-contra.yml new file mode 100644 index 0000000000..65f098fcc6 --- /dev/null +++ b/tests/regression/56-witness/18-base-unassume-contra.yml @@ -0,0 +1,28 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: e9cde5db-0c69-4413-ad46-7a15b93f1834 + creation_time: 2022-08-30T15:00:25Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-ge4b5d9f12-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''18-base-unassume-contra.c'' + ''--enable'' ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--set'' + ''printstats'' ''true'' ''--set'' ''goblint-dir'' ''.goblint-56-18''' + task: + input_files: + - 18-base-unassume-contra.c + input_file_hashes: + 18-base-unassume-contra.c: e173c414639b0c08d00854dc6974f87ccf2ae29829c2e5542f984eb8a52929ce + data_model: LP64 + language: C + location: + file_name: 18-base-unassume-contra.c + file_hash: e173c414639b0c08d00854dc6974f87ccf2ae29829c2e5542f984eb8a52929ce + line: 8 + column: 2 + function: main + loop_invariant: + string: i + 1 <= i + type: assertion + format: C From df5030bb29d1e09fed042715748932d485fb2ef8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 31 Aug 2022 11:47:44 +0300 Subject: [PATCH 026/132] Extract base invariant to BaseInvariant module --- src/analyses/base.ml | 567 +++------------------------------- src/analyses/baseInvariant.ml | 546 ++++++++++++++++++++++++++++++++ 2 files changed, 589 insertions(+), 524 deletions(-) create mode 100644 src/analyses/baseInvariant.ml diff --git a/src/analyses/base.ml b/src/analyses/base.ml index dc2fc70ac5..e51092f37e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1551,124 +1551,16 @@ struct | `Bot -> false (* HACK: bot is here due to typing conflict (we do not cast appropriately) *) | _ -> VD.is_bot_value x - let invariant_fallback ctx a (gs:glob_fun) st exp tv = - (* We use a recursive helper function so that x != 0 is false can be handled - * as x == 0 is true etc *) - let rec helper (op: binop) (lval: lval) (value: value) (tv: bool) = - match (op, lval, value, tv) with - (* The true-branch where x == value: *) - | Eq, x, value, true -> - if M.tracing then M.tracec "invariant" "Yes, %a equals %a\n" d_lval x VD.pretty value; - (match value with - | `Int n -> - let ikind = Cilfacade.get_ikind_exp (Lval lval) in - Some (x, `Int (ID.cast_to ikind n)) - | _ -> Some(x, value)) - (* The false-branch for x == value: *) - | Eq, x, value, false -> begin - match value with - | `Int n -> begin - match ID.to_int n with - | Some n -> - (* When x != n, we can return a singleton exclusion set *) - if M.tracing then M.tracec "invariant" "Yes, %a is not %s\n" d_lval x (BI.to_string n); - let ikind = Cilfacade.get_ikind_exp (Lval lval) in - Some (x, `Int (ID.of_excl_list ikind [n])) - | None -> None - end - | `Address n -> begin - if M.tracing then M.tracec "invariant" "Yes, %a is not %a\n" d_lval x AD.pretty n; - match eval_rv_address a gs st (Lval x) with - | `Address a when AD.is_definite n -> - Some (x, `Address (AD.diff a n)) - | `Top when AD.is_null n -> - Some (x, `Address AD.not_null) - | v -> - if M.tracing then M.tracec "invariant" "No address invariant for: %a != %a\n" VD.pretty v AD.pretty n; - None - end - (* | `Address a -> Some (x, value) *) - | _ -> - (* We can't say anything else, exclusion sets are finite, so not - * being in one means an infinite number of values *) - if M.tracing then M.tracec "invariant" "Failed! (not a definite value)\n"; - None - end - | Ne, x, value, _ -> helper Eq x value (not tv) - | Lt, x, value, _ -> begin - match value with - | `Int n -> begin - let ikind = Cilfacade.get_ikind_exp (Lval lval) in - let n = ID.cast_to ikind n in - let range_from x = if tv then ID.ending ikind (BI.sub x BI.one) else ID.starting ikind x in - let limit_from = if tv then ID.maximal else ID.minimal in - match limit_from n with - | Some n -> - if M.tracing then M.tracec "invariant" "Yes, success! %a is not %s\n\n" d_lval x (BI.to_string n); - Some (x, `Int (range_from n)) - | None -> None - end - | _ -> None - end - | Le, x, value, _ -> begin - match value with - | `Int n -> begin - let ikind = Cilfacade.get_ikind_exp (Lval lval) in - let n = ID.cast_to ikind n in - let range_from x = if tv then ID.ending ikind x else ID.starting ikind (BI.add x BI.one) in - let limit_from = if tv then ID.maximal else ID.minimal in - match limit_from n with - | Some n -> - if M.tracing then M.tracec "invariant" "Yes, success! %a is not %s\n\n" d_lval x (BI.to_string n); - Some (x, `Int (range_from n)) - | None -> None - end - | _ -> None - end - | Gt, x, value, _ -> helper Le x value (not tv) - | Ge, x, value, _ -> helper Lt x value (not tv) - | _ -> - if M.tracing then M.trace "invariant" "Failed! (operation not supported)\n\n"; - None - in - if M.tracing then M.traceli "invariant" "assume expression %a is %B\n" d_exp exp tv; - let null_val typ = - match Cil.unrollType typ with - | TPtr _ -> `Address AD.null_ptr - | TEnum({ekind=_;_},_) - | _ -> `Int (ID.of_int (Cilfacade.get_ikind typ) BI.zero) - in - let rec derived_invariant exp tv = - let switchedOp = function Lt -> Gt | Gt -> Lt | Le -> Ge | Ge -> Le | x -> x in (* a op b <=> b (switchedOp op) b *) - match exp with - (* Since we handle not only equalities, the order is important *) - | BinOp(op, Lval x, rval, typ) -> helper op x (VD.cast (Cilfacade.typeOfLval x) (eval_rv a gs st rval)) tv - | BinOp(op, rval, Lval x, typ) -> derived_invariant (BinOp(switchedOp op, Lval x, rval, typ)) tv - | BinOp(op, CastE (t1, c1), CastE (t2, c2), t) when (op = Eq || op = Ne) && typeSig t1 = typeSig t2 && VD.is_safe_cast t1 (Cilfacade.typeOf c1) && VD.is_safe_cast t2 (Cilfacade.typeOf c2) - -> derived_invariant (BinOp (op, c1, c2, t)) tv - | BinOp(op, CastE (TInt (ik, _) as t1, Lval x), rval, typ) -> - (match eval_rv a gs st (Lval x) with - | `Int v -> - (* This is tricky: It it is not sufficient to check that ID.cast_to_ik v = v - * If there is one domain that knows this to be true and the other does not, we - * should still impose the invariant. E.g. i -> ([1,5]; Not {0}[byte]) *) - if VD.is_safe_cast t1 (Cilfacade.typeOfLval x) then - derived_invariant (BinOp (op, Lval x, rval, typ)) tv - else - None - | _ -> None) - | BinOp(op, rval, CastE (TInt (_, _) as ti, Lval x), typ) -> - derived_invariant (BinOp (switchedOp op, CastE(ti, Lval x), rval, typ)) tv - (* Cases like if (x) are treated like if (x != 0) *) - | Lval x -> - (* There are two correct ways of doing it: "if ((int)x != 0)" or "if (x != (typeof(x))0))" - * Because we try to avoid casts (and use a more precise address domain) we use the latter *) - helper Ne x (null_val (Cilfacade.typeOf exp)) tv - | UnOp (LNot,uexp,typ) -> derived_invariant uexp (not tv) - | _ -> - if M.tracing then M.tracec "invariant" "Failed! (expression %a not understood)\n\n" d_plainexp exp; - None - in + module InvariantEval = + struct + module D = D + module V = V + module G = G + + let eval_rv = eval_rv + let eval_rv_address = eval_rv_address + let eval_lv = eval_lv + let apply_invariant oldv newv = match oldv, newv with (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o && AD.mem (Addr.unknown_ptr ()) n -> *) @@ -1676,9 +1568,8 @@ struct (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o -> `Address n *) (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) n -> `Address o *) | _ -> VD.meet oldv newv - in - match derived_invariant exp tv with - | Some (lval, value) -> + + let refine_lv_fallback ctx a gs st lval value tv = if M.tracing then M.tracec "invariant" "Restricting %a with %a\n" d_lval lval VD.pretty value; let addr = eval_lv a gs st lval in if (AD.is_top addr) then st @@ -1698,410 +1589,38 @@ struct else if VD.is_bot new_val then set a gs st addr t_lval value ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) else set a gs st addr t_lval new_val ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) - | None -> - if M.tracing then M.traceu "invariant" "Doing nothing.\n"; - M.debug ~category:Analyzer "Invariant failed: expression \"%a\" not understood." d_plainexp exp; - st - - let invariant ctx a gs st exp tv: store = - let fallback reason st = - if M.tracing then M.tracel "inv" "Can't handle %a.\n%s\n" d_plainexp exp reason; - invariant_fallback ctx a gs st exp tv - in - (* inverse values for binary operation a `op` b == c *) - (* ikind is the type of a for limiting ranges of the operands a, b. The only binops which can have different types for a, b are Shiftlt, Shiftrt (not handled below; don't use ikind to limit b there). *) - let inv_bin_int (a, b) ikind c op = - let warn_and_top_on_zero x = - if GobOption.exists (BI.equal BI.zero) (ID.to_int x) then - (M.error ~category:M.Category.Integer.div_by_zero ~tags:[CWE 369] "Must Undefined Behavior: Second argument of div or mod is 0, continuing with top"; - ID.top_of ikind) - else - x - in - let meet_bin a' b' = ID.meet a a', ID.meet b b' in - let meet_com oi = (* commutative *) - try - meet_bin (oi c b) (oi c a) - with - IntDomain.ArithmeticOnIntegerBot _ -> raise Deadcode in - let meet_non oi oo = (* non-commutative *) - try - meet_bin (oi c b) (oo a c) - with IntDomain.ArithmeticOnIntegerBot _ -> raise Deadcode in - match op with - | PlusA -> meet_com ID.sub - | Mult -> - (* Only multiplication with odd numbers is an invertible operation in (mod 2^n) *) - (* refine x by information about y, using x * y == c *) - let refine_by x y = (match ID.to_int y with - | None -> x - | Some v when BI.equal (BI.rem v (BI.of_int 2)) BI.zero (* v % 2 = 0 *) -> x (* A refinement would still be possible here, but has to take non-injectivity into account. *) - | Some v (* when Int64.rem v 2L = 1L *) -> ID.meet x (ID.div c y)) (* Div is ok here, c must be divisible by a and b *) - in - (refine_by a b, refine_by b a) - | MinusA -> meet_non ID.add ID.sub - | Div -> - (* If b must be zero, we have must UB *) - let b = warn_and_top_on_zero b in - (* Integer division means we need to add the remainder, so instead of just `a = c*b` we have `a = c*b + a%b`. - * However, a%b will give [-b+1, b-1] for a=top, but we only want the positive/negative side depending on the sign of c*b. - * If c*b = 0 or it can be positive or negative, we need the full range for the remainder. *) - let rem = - let is_pos = ID.to_bool @@ ID.gt (ID.mul b c) (ID.of_int ikind BI.zero) = Some true in - let is_neg = ID.to_bool @@ ID.lt (ID.mul b c) (ID.of_int ikind BI.zero) = Some true in - let full = ID.rem a b in - if is_pos then ID.meet (ID.starting ikind BI.zero) full - else if is_neg then ID.meet (ID.ending ikind BI.zero) full - else full - in - meet_bin (ID.add (ID.mul b c) rem) (ID.div (ID.sub a rem) c) - | Mod -> (* a % b == c *) - (* If b must be zero, we have must UB *) - let b = warn_and_top_on_zero b in - (* a' = a/b*b + c and derived from it b' = (a-c)/(a/b) - * The idea is to formulate a' as quotient * divisor + remainder. *) - let a' = ID.add (ID.mul (ID.div a b) b) c in - let b' = ID.div (ID.sub a c) (ID.div a b) in - (* However, for [2,4]%2 == 1 this only gives [3,4]. - * If the upper bound of a is divisible by b, we can also meet with the result of a/b*b - c to get the precise [3,3]. - * If b is negative we have to look at the lower bound. *) - let is_divisible bound = - match bound a with - | Some ba -> ID.rem (ID.of_int ikind ba) b |> ID.to_int = Some BI.zero - | None -> false - in - let max_pos = match ID.maximal b with None -> true | Some x -> BI.compare x BI.zero >= 0 in - let min_neg = match ID.minimal b with None -> true | Some x -> BI.compare x BI.zero < 0 in - let implies a b = not a || b in - let a'' = - if implies max_pos (is_divisible ID.maximal) && implies min_neg (is_divisible ID.minimal) then - ID.meet a' (ID.sub (ID.mul (ID.div a b) b) c) - else a' - in - let a''' = - (* if both b and c are definite, we can get a precise value in the congruence domain *) - if ID.is_int b && ID.is_int c then - (* a%b == c -> a: c+bℤ *) - let t = ID.of_congruence ikind ((BatOption.get @@ ID.to_int c), (BatOption.get @@ ID.to_int b)) in - ID.meet a'' t - else a'' - in - meet_bin a''' b' - | Eq | Ne as op -> - let both x = x, x in - let m = ID.meet a b in - (match op, ID.to_bool c with - | Eq, Some true - | Ne, Some false -> both m (* def. equal: if they compare equal, both values must be from the meet *) - | Eq, Some false - | Ne, Some true -> (* def. unequal *) - (* Both values can not be in the meet together, but it's not sound to exclude the meet from both. - * e.g. a=[0,1], b=[1,2], meet a b = [1,1], but (a != b) does not imply a=[0,0], b=[2,2] since others are possible: a=[1,1], b=[2,2] - * Only if a is a definite value, we can exclude it from b: *) - let excl a b = match ID.to_int a with Some x -> ID.of_excl_list ikind [x] | None -> b in - let a' = excl b a in - let b' = excl a b in - if M.tracing then M.tracel "inv" "inv_bin_int: unequal: %a and %a; ikind: %a; a': %a, b': %a\n" ID.pretty a ID.pretty b d_ikind ikind ID.pretty a' ID.pretty b'; - meet_bin a' b' - | _, _ -> a, b + + let refine_lv ctx a gs st c x c' pretty exp = + let eval e st = eval_rv a gs st e in + let set' lval v st = set a gs st (eval_lv a gs st lval) (Cilfacade.typeOfLval lval) v ~invariant:true ~ctx in + match x with + | Var var, o -> + (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) + let oldv = get_var a gs st var in + let offs = convert_offset a gs st o in + let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in + let v = VD.meet oldv newv in + if is_some_bot v then raise Deadcode + else ( + if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v pretty c VD.pretty c'; + let r = set' (Var var,NoOffset) v st in + if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; + r ) - | Lt | Le | Ge | Gt as op -> - let pred x = BI.sub x BI.one in - let succ x = BI.add x BI.one in - (match ID.minimal a, ID.maximal a, ID.minimal b, ID.maximal b with - | Some l1, Some u1, Some l2, Some u2 -> - (* if M.tracing then M.tracel "inv" "Op: %s, l1: %Ld, u1: %Ld, l2: %Ld, u2: %Ld\n" (show_binop op) l1 u1 l2 u2; *) - (match op, ID.to_bool c with - | Le, Some true - | Gt, Some false -> meet_bin (ID.ending ikind u2) (ID.starting ikind l1) - | Ge, Some true - | Lt, Some false -> meet_bin (ID.starting ikind l2) (ID.ending ikind u1) - | Lt, Some true - | Ge, Some false -> meet_bin (ID.ending ikind (pred u2)) (ID.starting ikind (succ l1)) - | Gt, Some true - | Le, Some false -> meet_bin (ID.starting ikind (succ l2)) (ID.ending ikind (pred u1)) - | _, _ -> a, b) - | _ -> a, b) - | BOr | BXor as op-> - if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; - (* Be careful: inv_exp performs a meet on both arguments of the BOr / BXor. *) - a, b - | LAnd -> - if ID.to_bool c = Some true then - meet_bin c c - else - a, b - | op -> - if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; - a, b - in - let inv_bin_float (a, b) c op = - let open Stdlib in - let meet_bin a' b' = FD.meet a a', FD.meet b b' in - (* Refining the abstract values based on branching is roughly based on the idea in [Symbolic execution of floating-point computations](https://hal.inria.fr/inria-00540299/document) - However, their approach is only applicable to the "nearest" rounding mode. Here we use a more general approach covering all possible rounding modes and therefore - use the actual `pred c_min`/`succ c_max` for the outer-bounds instead of the middles between `c_min` and `pred c_min`/`c_max` and `succ c_max` as suggested in the paper. - This also removes the necessity of computing those expressions with higher precise than in the concrete. - *) - try - match op with - | PlusA -> - (* A + B = C, \forall a \in A. a + b_min > pred c_min \land a + b_max < succ c_max - \land a + b_max > pred c_min \land a + b_min < succ c_max - \rightarrow A = [min(pred c_min - b_min, pred c_min - b_max), max(succ c_max - b_max, succ c_max - b_min)] - \rightarrow A = [pred c_min - b_max, succ c_max - b_min] - *) - let reverse_add v v' = (match FD.minimal c, FD.maximal c, FD.minimal v, FD.maximal v with - | Some c_min, Some c_max, Some v_min, Some v_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> - let l = Float.pred c_min -. v_max in - let h = Float.succ c_max -. v_min in - FD.of_interval (FD.get_fkind c) (l, h) - | _ -> v') in - meet_bin (reverse_add b a) (reverse_add a b) - | MinusA -> - (* A - B = C \ forall a \in A. a - b_max > pred c_min \land a - b_min < succ c_max - \land a - b_min > pred c_min \land a - b_max < succ c_max - \rightarrow A = [min(pred c_min + b_max, pred c_min + b_min), max(succ c_max + b_max, succ c_max + b_max)] - \rightarrow A = [pred c_min + b_min, succ c_max + b_max] - *) - let a' = (match FD.minimal c, FD.maximal c, FD.minimal b, FD.maximal b with - | Some c_min, Some c_max, Some b_min, Some b_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> - let l = Float.pred c_min +. b_min in - let h = Float.succ c_max +. b_max in - FD.of_interval (FD.get_fkind c) (l, h) - | _ -> a) in - (* A - B = C \ forall b \in B. a_min - b > pred c_min \land a_max - b < succ c_max - \land a_max - b > pred c_min \land a_min - b < succ c_max - \rightarrow B = [min(a_max - succ c_max, a_min - succ c_max), max(a_min - pred c_min, a_max - pred c_min)] - \rightarrow B = [a_min - succ c_max, a_max - pred c_min] - *) - let b' = (match FD.minimal c, FD.maximal c, FD.minimal a, FD.maximal a with - | Some c_min, Some c_max, Some a_min, Some a_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> - let l = a_min -. Float.succ c_max in - let h = a_max -. Float.pred c_min in - FD.of_interval (FD.get_fkind c) (l, h) - | _ -> b) in - meet_bin a' b' - | Mult -> - (* A * B = C \forall a \in A, a > 0. a * b_min > pred c_min \land a * b_max < succ c_max - A * B = C \forall a \in A, a < 0. a * b_max > pred c_min \land a * b_min < succ c_max - (with negative b reversed <>) - \rightarrow A = [min(pred c_min / b_min, pred c_min / b_max, succ c_max / b_min, succ c_max /b_max), - max(succ c_max / b_min, succ c_max /b_max, pred c_min / b_min, pred c_min / b_max)] - *) - let reverse_mul v v' = (match FD.minimal c, FD.maximal c, FD.minimal v, FD.maximal v with - | Some c_min, Some c_max, Some v_min, Some v_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> - let v1, v2, v3, v4 = (Float.pred c_min /. v_min), (Float.pred c_min /. v_max), (Float.succ c_max /. v_min), (Float.succ c_max /. v_max) in - let l = Float.min (Float.min v1 v2) (Float.min v3 v4) in - let h = Float.max (Float.max v1 v2) (Float.max v3 v4) in - FD.of_interval (FD.get_fkind c) (l, h) - | _ -> v') in - meet_bin (reverse_mul b a) (reverse_mul a b) - | Div -> - (* A / B = C \forall a \in A, a > 0, b_min > 1. a / b_max > pred c_min \land a / b_min < succ c_max - A / B = C \forall a \in A, a < 0, b_min > 1. a / b_min > pred c_min \land a / b_max < succ c_max - A / B = C \forall a \in A, a > 0, 0 < b_min, b_max < 1. a / b_max > pred c_min \land a / b_min < succ c_max - A / B = C \forall a \in A, a < 0, 0 < b_min, b_max < 1. a / b_min > pred c_min \land a / b_max < succ c_max - ... same for negative b - \rightarrow A = [min(b_max * pred c_min, b_min * pred c_min, b_min * succ c_max, b_max * succ c_max), - max(b_max * succ c_max, b_min * succ c_max, b_max * pred c_min, b_min * pred c_min)] - *) - let a' = (match FD.minimal c, FD.maximal c, FD.minimal b, FD.maximal b with - | Some c_min, Some c_max, Some b_min, Some b_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> - let v1, v2, v3, v4 = (Float.pred c_min *. b_max), (Float.pred c_min *. b_min), (Float.succ c_max *. b_max), (Float.succ c_max *. b_min) in - let l = Float.min (Float.min v1 v2) (Float.min v3 v4) in - let h = Float.max (Float.max v1 v2) (Float.max v3 v4) in - FD.of_interval (FD.get_fkind c) (l, h) - | _ -> a) in - (* A / B = C \forall b \in B, b > 0, a_min / b > pred c_min \land a_min / b < succ c_max - \land a_max / b > pred c_min \land a_max / b < succ c_max - A / B = C \forall b \in B, b < 0, a_min / b > pred c_min \land a_min / b < succ c_max - \land a_max / b > pred c_min \land a_max / b < succ c_max - \rightarrow (b != 0) B = [min(a_min / succ c_max, a_max / succ c_max, a_min / pred c_min, a_max / pred c_min), - max(a_min / pred c_min, a_max / pred c_min, a_min / succ c_max, a_max / succ c_max)] - *) - let b' = (match FD.minimal c, FD.maximal c, FD.minimal a, FD.maximal a with - | Some c_min, Some c_max, Some a_min, Some a_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> - let v1, v2, v3, v4 = (a_min /. Float.pred c_min), (a_max /. Float.pred c_min), (a_min /. Float.succ c_max), (a_max /. Float.succ c_max) in - let l = Float.min (Float.min v1 v2) (Float.min v3 v4) in - let h = Float.max (Float.max v1 v2) (Float.max v3 v4) in - FD.of_interval (FD.get_fkind c) (l, h) - | _ -> b) in - meet_bin a' b' - | Eq | Ne as op -> - let both x = x, x in - (match op, ID.to_bool (FD.to_int IBool c) with - | Eq, Some true - | Ne, Some false -> both (FD.meet a b) (* def. equal: if they compare equal, both values must be from the meet *) - | Eq, Some false - | Ne, Some true -> (* def. unequal *) - (* M.debug ~category:Analyzer "Can't use unequal information about float value in expression \"%a\"." d_plainexp exp; *) - a, b - | _, _ -> a, b - ) - | Lt | Le | Ge | Gt as op -> - (match FD.minimal a, FD.maximal a, FD.minimal b, FD.maximal b with - | Some l1, Some u1, Some l2, Some u2 -> - (match op, ID.to_bool (FD.to_int IBool c) with - | Le, Some true - | Gt, Some false -> meet_bin (FD.ending (FD.get_fkind a) u2) (FD.starting (FD.get_fkind b) l1) - | Ge, Some true - | Lt, Some false -> meet_bin (FD.starting (FD.get_fkind a) l2) (FD.ending (FD.get_fkind b) u1) - | Lt, Some true - | Ge, Some false -> meet_bin (FD.ending_before (FD.get_fkind a) u2) (FD.starting_after (FD.get_fkind b) l1) - | Gt, Some true - | Le, Some false -> meet_bin (FD.starting_after (FD.get_fkind a) l2) (FD.ending_before (FD.get_fkind b) u1) - | _, _ -> a, b) - | _ -> a, b) - | op -> - if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; - a, b - with FloatDomain.ArithmeticOnFloatBot _ -> raise Deadcode - in - let eval e st = eval_rv a gs st e in - let eval_bool e st = match eval e st with `Int i -> ID.to_bool i | _ -> None in - let set' lval v st = set a gs st (eval_lv a gs st lval) (Cilfacade.typeOfLval lval) v ~invariant:true ~ctx in - let rec inv_exp c_typed exp (st:store): store = - (* trying to improve variables in an expression so it is bottom means dead code *) - if VD.is_bot_value c_typed then raise Deadcode; - match exp, c_typed with - | UnOp (LNot, e, _), `Int c -> - let ikind = Cilfacade.get_ikind_exp e in - let c' = - match ID.to_bool (unop_ID LNot c) with - | Some true -> - (* i.e. e should evaluate to [1,1] *) - (* LNot x is 0 for any x != 0 *) - ID.of_excl_list ikind [BI.zero] - | Some false -> ID.of_bool ikind false - | _ -> ID.top_of ikind - in - inv_exp (`Int c') e st - | UnOp (Neg, e, _), `Float c -> inv_exp (`Float (unop_FD Neg c)) e st - | UnOp ((BNot|Neg) as op, e, _), `Int c -> inv_exp (`Int (unop_ID op c)) e st - (* no equivalent for `Float, as VD.is_safe_cast fails for all float types anyways *) - | BinOp(op, CastE (t1, c1), CastE (t2, c2), t), `Int c when (op = Eq || op = Ne) && typeSig (Cilfacade.typeOf c1) = typeSig (Cilfacade.typeOf c2) && VD.is_safe_cast t1 (Cilfacade.typeOf c1) && VD.is_safe_cast t2 (Cilfacade.typeOf c2) -> - inv_exp (`Int c) (BinOp (op, c1, c2, t)) st - | (BinOp (op, e1, e2, _) as e, `Float _) - | (BinOp (op, e1, e2, _) as e, `Int _) -> - let invert_binary_op c pretty c_int c_float = - if M.tracing then M.tracel "inv" "binop %a with %a %a %a == %a\n" d_exp e VD.pretty (eval e1 st) d_binop op VD.pretty (eval e2 st) pretty c; - (match eval e1 st, eval e2 st with - | `Int a, `Int b -> - let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) - let a', b' = inv_bin_int (a, b) ikind (c_int ikind) op in - if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; - let st' = inv_exp (`Int a') e1 st in - let st'' = inv_exp (`Int b') e2 st' in - st'' - | `Float a, `Float b -> - let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) - let a', b' = inv_bin_float (a, b) (c_float fkind) op in - if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; - let st' = inv_exp (`Float a') e1 st in - let st'' = inv_exp (`Float b') e2 st' in - st'' - (* Mixed `Float and `Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) - | `Int _, `Float _ | `Float _, `Int _ -> failwith "ill-typed program"; - (* | `Address a, `Address b -> ... *) - | a1, a2 -> fallback ("binop: got abstract values that are not `Int: " ^ sprint VD.pretty a1 ^ " and " ^ sprint VD.pretty a2) st) - (* use closures to avoid unused casts *) - in (match c_typed with - | `Int c -> invert_binary_op c ID.pretty (fun ik -> ID.cast_to ik c) (fun fk -> FD.of_int fk c) - | `Float c -> invert_binary_op c FD.pretty (fun ik -> FD.to_int ik c) (fun fk -> FD.cast_to fk c) - | _ -> failwith "unreachable") - | Lval x, `Int _(* meet x with c *) - | Lval x, `Float _ -> (* meet x with c *) - let update_lval c x c' pretty = (match x with - | Var var, o -> - (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) - let oldv = get_var a gs st var in - let offs = convert_offset a gs st o in - let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in - let v = VD.meet oldv newv in - if is_some_bot v then raise Deadcode - else ( - if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v pretty c VD.pretty c'; - let r = set' (Var var,NoOffset) v st in - if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; - r - ) - | Mem _, _ -> - (* For accesses via pointers, not yet *) - let oldv = eval (Lval x) st in - let v = VD.meet oldv c' in - if is_some_bot v then raise Deadcode - else ( - if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; - set' x v st - )) in - let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) - (match c_typed with - | `Int c -> update_lval c x (match t with - | TPtr _ -> `Address (AD.of_int (module ID) c) - | TInt (ik, _) - | TEnum ({ekind = ik; _}, _) -> `Int (ID.cast_to ik c) - | TFloat (fk, _) -> `Float (FD.of_int fk c) - | _ -> `Int c) ID.pretty - | `Float c -> update_lval c x (match t with - (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) - | TInt (ik, _) -> `Int (FD.to_int ik c) - (* this is theoretically possible and should be handled correctly, however i can't imagine an actual piece of c code producing this?! *) - | TEnum ({ekind = ik; _}, _) -> `Int (FD.to_int ik c) - | TFloat (fk, _) -> `Float (FD.cast_to fk c) - | _ -> `Float c) FD.pretty - | _ -> failwith "unreachable") - | Const _ , _ -> st (* nothing to do *) - | CastE ((TFloat (_, _)), e), `Float c -> - (match Cilfacade.typeOf e, FD.get_fkind c with - | TFloat (FLongDouble as fk, _), FFloat - | TFloat (FDouble as fk, _), FFloat - | TFloat (FLongDouble as fk, _), FDouble - | TFloat (fk, _), FLongDouble - | TFloat (FDouble as fk, _), FDouble - | TFloat (FFloat as fk, _), FFloat -> inv_exp (`Float (FD.cast_to fk c)) e st - | _ -> fallback ("CastE: incompatible types") st) - | CastE ((TInt (ik, _)) as t, e), `Int c - | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e), `Int c -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) - (match eval e st with - | `Int i -> - if ID.leq i (ID.cast_to ik i) then - match Cilfacade.typeOf e with - | TInt(ik_e, _) - | TEnum ({ekind = ik_e; _ }, _) -> - let c' = ID.cast_to ik_e c in - if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; - inv_exp (`Int c') e st - | x -> fallback ("CastE: e did evaluate to `Int, but the type did not match" ^ sprint d_type t) st - else - fallback ("CastE: " ^ sprint d_plainexp e ^ " evaluates to " ^ sprint ID.pretty i ^ " which is bigger than the type it is cast to which is " ^ sprint d_type t) st - | v -> fallback ("CastE: e did not evaluate to `Int, but " ^ sprint VD.pretty v) st) - | e, _ -> fallback (sprint d_plainexp e ^ " not implemented") st - in - if eval_bool exp st = Some (not tv) then raise Deadcode (* we already know that the branch is dead *) - else - (* C11 6.5.13, 6.5.14, 6.5.3.3: LAnd, LOr and LNot also return 0 or 1 *) - let is_cmp = function - | UnOp (LNot, _, _) - | BinOp ((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr), _, _, _) -> true - | _ -> false - in - try - let ik = Cilfacade.get_ikind_exp exp in - let itv = if not tv || is_cmp exp then (* false is 0, but true can be anything that is not 0, except for comparisons which yield 1 *) - ID.of_bool ik tv (* this will give 1 for true which is only ok for comparisons *) - else - ID.of_excl_list ik [BI.zero] (* Lvals, Casts, arithmetic operations etc. should work with true = non_zero *) - in - inv_exp (`Int itv) exp st - with Invalid_argument _ -> - let fk = Cilfacade.get_fkind_exp exp in - let ftv = if not tv then (* false is 0, but true can be anything that is not 0, except for comparisons which yield 1 *) - FD.of_const fk 0. - else - FD.top_of fk - in - inv_exp (`Float ftv) exp st + | Mem _, _ -> + (* For accesses via pointers, not yet *) + let oldv = eval (Lval x) st in + let v = VD.meet oldv c' in + if is_some_bot v then raise Deadcode + else ( + if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; + set' x v st + ) + end + + module Invariant = BaseInvariant.Make (InvariantEval) + + let invariant = Invariant.invariant let set_savetop ~ctx ?lval_raw ?rval_raw ask (gs:glob_fun) st adr lval_t v : store = diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml new file mode 100644 index 0000000000..ba75e3d0d2 --- /dev/null +++ b/src/analyses/baseInvariant.ml @@ -0,0 +1,546 @@ +open Prelude.Ana +open GoblintCil + +module M = Messages +module VD = BaseDomain.VD +module ID = ValueDomain.ID +module FD = ValueDomain.FD +module AD = ValueDomain.AD +module BI = IntOps.BigIntOps + +module type Eval = +sig + module D: Lattice.S + module V: Analyses.SpecSysVar + module G: Lattice.S + + val eval_rv: Queries.ask -> (V.t -> G.t) -> D.t -> exp -> VD.t + val eval_rv_address: Queries.ask -> (V.t -> G.t) -> D.t -> exp -> VD.t + val eval_lv: Queries.ask -> (V.t -> G.t) -> D.t -> lval -> AD.t + + val refine_lv_fallback: (D.t, G.t, _, V.t) Analyses.ctx -> Queries.ask -> (V.t -> G.t) -> D.t -> lval -> VD.t -> bool -> D.t + val refine_lv: (D.t, G.t, _, V.t) Analyses.ctx -> Queries.ask -> (V.t -> G.t) -> D.t -> 'a -> lval -> VD.t -> (unit -> 'a -> doc) -> exp -> D.t +end + +module Make (Eval: Eval) = +struct + open Eval + + let unop_ID = function + | Neg -> ID.neg + | BNot -> ID.bitnot + | LNot -> ID.lognot + + let unop_FD = function + | Neg -> FD.neg + (* other unary operators are not implemented on float values *) + | _ -> (fun c -> FD.top_of (FD.get_fkind c)) + + let is_some_bot x = + match x with + | `Bot -> false (* HACK: bot is here due to typing conflict (we do not cast appropriately) *) + | _ -> VD.is_bot_value x + + let invariant_fallback ctx a (gs:V.t -> G.t) st exp tv = + (* We use a recursive helper function so that x != 0 is false can be handled + * as x == 0 is true etc *) + let rec helper (op: binop) (lval: lval) (value: VD.t) (tv: bool) = + match (op, lval, value, tv) with + (* The true-branch where x == value: *) + | Eq, x, value, true -> + if M.tracing then M.tracec "invariant" "Yes, %a equals %a\n" d_lval x VD.pretty value; + (match value with + | `Int n -> + let ikind = Cilfacade.get_ikind_exp (Lval lval) in + Some (x, `Int (ID.cast_to ikind n)) + | _ -> Some(x, value)) + (* The false-branch for x == value: *) + | Eq, x, value, false -> begin + match value with + | `Int n -> begin + match ID.to_int n with + | Some n -> + (* When x != n, we can return a singleton exclusion set *) + if M.tracing then M.tracec "invariant" "Yes, %a is not %s\n" d_lval x (BI.to_string n); + let ikind = Cilfacade.get_ikind_exp (Lval lval) in + Some (x, `Int (ID.of_excl_list ikind [n])) + | None -> None + end + | `Address n -> begin + if M.tracing then M.tracec "invariant" "Yes, %a is not %a\n" d_lval x AD.pretty n; + match eval_rv_address a gs st (Lval x) with + | `Address a when AD.is_definite n -> + Some (x, `Address (AD.diff a n)) + | `Top when AD.is_null n -> + Some (x, `Address AD.not_null) + | v -> + if M.tracing then M.tracec "invariant" "No address invariant for: %a != %a\n" VD.pretty v AD.pretty n; + None + end + (* | `Address a -> Some (x, value) *) + | _ -> + (* We can't say anything else, exclusion sets are finite, so not + * being in one means an infinite number of values *) + if M.tracing then M.tracec "invariant" "Failed! (not a definite value)\n"; + None + end + | Ne, x, value, _ -> helper Eq x value (not tv) + | Lt, x, value, _ -> begin + match value with + | `Int n -> begin + let ikind = Cilfacade.get_ikind_exp (Lval lval) in + let n = ID.cast_to ikind n in + let range_from x = if tv then ID.ending ikind (BI.sub x BI.one) else ID.starting ikind x in + let limit_from = if tv then ID.maximal else ID.minimal in + match limit_from n with + | Some n -> + if M.tracing then M.tracec "invariant" "Yes, success! %a is not %s\n\n" d_lval x (BI.to_string n); + Some (x, `Int (range_from n)) + | None -> None + end + | _ -> None + end + | Le, x, value, _ -> begin + match value with + | `Int n -> begin + let ikind = Cilfacade.get_ikind_exp (Lval lval) in + let n = ID.cast_to ikind n in + let range_from x = if tv then ID.ending ikind x else ID.starting ikind (BI.add x BI.one) in + let limit_from = if tv then ID.maximal else ID.minimal in + match limit_from n with + | Some n -> + if M.tracing then M.tracec "invariant" "Yes, success! %a is not %s\n\n" d_lval x (BI.to_string n); + Some (x, `Int (range_from n)) + | None -> None + end + | _ -> None + end + | Gt, x, value, _ -> helper Le x value (not tv) + | Ge, x, value, _ -> helper Lt x value (not tv) + | _ -> + if M.tracing then M.trace "invariant" "Failed! (operation not supported)\n\n"; + None + in + if M.tracing then M.traceli "invariant" "assume expression %a is %B\n" d_exp exp tv; + let null_val typ = + match Cil.unrollType typ with + | TPtr _ -> `Address AD.null_ptr + | TEnum({ekind=_;_},_) + | _ -> `Int (ID.of_int (Cilfacade.get_ikind typ) BI.zero) + in + let rec derived_invariant exp tv = + let switchedOp = function Lt -> Gt | Gt -> Lt | Le -> Ge | Ge -> Le | x -> x in (* a op b <=> b (switchedOp op) b *) + match exp with + (* Since we handle not only equalities, the order is important *) + | BinOp(op, Lval x, rval, typ) -> helper op x (VD.cast (Cilfacade.typeOfLval x) (eval_rv a gs st rval)) tv + | BinOp(op, rval, Lval x, typ) -> derived_invariant (BinOp(switchedOp op, Lval x, rval, typ)) tv + | BinOp(op, CastE (t1, c1), CastE (t2, c2), t) when (op = Eq || op = Ne) && typeSig t1 = typeSig t2 && VD.is_safe_cast t1 (Cilfacade.typeOf c1) && VD.is_safe_cast t2 (Cilfacade.typeOf c2) + -> derived_invariant (BinOp (op, c1, c2, t)) tv + | BinOp(op, CastE (TInt (ik, _) as t1, Lval x), rval, typ) -> + (match eval_rv a gs st (Lval x) with + | `Int v -> + (* This is tricky: It it is not sufficient to check that ID.cast_to_ik v = v + * If there is one domain that knows this to be true and the other does not, we + * should still impose the invariant. E.g. i -> ([1,5]; Not {0}[byte]) *) + if VD.is_safe_cast t1 (Cilfacade.typeOfLval x) then + derived_invariant (BinOp (op, Lval x, rval, typ)) tv + else + None + | _ -> None) + | BinOp(op, rval, CastE (TInt (_, _) as ti, Lval x), typ) -> + derived_invariant (BinOp (switchedOp op, CastE(ti, Lval x), rval, typ)) tv + (* Cases like if (x) are treated like if (x != 0) *) + | Lval x -> + (* There are two correct ways of doing it: "if ((int)x != 0)" or "if (x != (typeof(x))0))" + * Because we try to avoid casts (and use a more precise address domain) we use the latter *) + helper Ne x (null_val (Cilfacade.typeOf exp)) tv + | UnOp (LNot,uexp,typ) -> derived_invariant uexp (not tv) + | _ -> + if M.tracing then M.tracec "invariant" "Failed! (expression %a not understood)\n\n" d_plainexp exp; + None + in + match derived_invariant exp tv with + | Some (lval, value) -> + refine_lv_fallback ctx a gs st lval value tv + | None -> + if M.tracing then M.traceu "invariant" "Doing nothing.\n"; + M.debug ~category:Analyzer "Invariant failed: expression \"%a\" not understood." d_plainexp exp; + st + + let invariant ctx a gs st exp tv: D.t = + let fallback reason st = + if M.tracing then M.tracel "inv" "Can't handle %a.\n%s\n" d_plainexp exp reason; + invariant_fallback ctx a gs st exp tv + in + (* inverse values for binary operation a `op` b == c *) + (* ikind is the type of a for limiting ranges of the operands a, b. The only binops which can have different types for a, b are Shiftlt, Shiftrt (not handled below; don't use ikind to limit b there). *) + let inv_bin_int (a, b) ikind c op = + let warn_and_top_on_zero x = + if GobOption.exists (BI.equal BI.zero) (ID.to_int x) then + (M.error ~category:M.Category.Integer.div_by_zero ~tags:[CWE 369] "Must Undefined Behavior: Second argument of div or mod is 0, continuing with top"; + ID.top_of ikind) + else + x + in + let meet_bin a' b' = ID.meet a a', ID.meet b b' in + let meet_com oi = (* commutative *) + try + meet_bin (oi c b) (oi c a) + with + IntDomain.ArithmeticOnIntegerBot _ -> raise Analyses.Deadcode in + let meet_non oi oo = (* non-commutative *) + try + meet_bin (oi c b) (oo a c) + with IntDomain.ArithmeticOnIntegerBot _ -> raise Analyses.Deadcode in + match op with + | PlusA -> meet_com ID.sub + | Mult -> + (* Only multiplication with odd numbers is an invertible operation in (mod 2^n) *) + (* refine x by information about y, using x * y == c *) + let refine_by x y = (match ID.to_int y with + | None -> x + | Some v when BI.equal (BI.rem v (BI.of_int 2)) BI.zero (* v % 2 = 0 *) -> x (* A refinement would still be possible here, but has to take non-injectivity into account. *) + | Some v (* when Int64.rem v 2L = 1L *) -> ID.meet x (ID.div c y)) (* Div is ok here, c must be divisible by a and b *) + in + (refine_by a b, refine_by b a) + | MinusA -> meet_non ID.add ID.sub + | Div -> + (* If b must be zero, we have must UB *) + let b = warn_and_top_on_zero b in + (* Integer division means we need to add the remainder, so instead of just `a = c*b` we have `a = c*b + a%b`. + * However, a%b will give [-b+1, b-1] for a=top, but we only want the positive/negative side depending on the sign of c*b. + * If c*b = 0 or it can be positive or negative, we need the full range for the remainder. *) + let rem = + let is_pos = ID.to_bool @@ ID.gt (ID.mul b c) (ID.of_int ikind BI.zero) = Some true in + let is_neg = ID.to_bool @@ ID.lt (ID.mul b c) (ID.of_int ikind BI.zero) = Some true in + let full = ID.rem a b in + if is_pos then ID.meet (ID.starting ikind BI.zero) full + else if is_neg then ID.meet (ID.ending ikind BI.zero) full + else full + in + meet_bin (ID.add (ID.mul b c) rem) (ID.div (ID.sub a rem) c) + | Mod -> (* a % b == c *) + (* If b must be zero, we have must UB *) + let b = warn_and_top_on_zero b in + (* a' = a/b*b + c and derived from it b' = (a-c)/(a/b) + * The idea is to formulate a' as quotient * divisor + remainder. *) + let a' = ID.add (ID.mul (ID.div a b) b) c in + let b' = ID.div (ID.sub a c) (ID.div a b) in + (* However, for [2,4]%2 == 1 this only gives [3,4]. + * If the upper bound of a is divisible by b, we can also meet with the result of a/b*b - c to get the precise [3,3]. + * If b is negative we have to look at the lower bound. *) + let is_divisible bound = + match bound a with + | Some ba -> ID.rem (ID.of_int ikind ba) b |> ID.to_int = Some BI.zero + | None -> false + in + let max_pos = match ID.maximal b with None -> true | Some x -> BI.compare x BI.zero >= 0 in + let min_neg = match ID.minimal b with None -> true | Some x -> BI.compare x BI.zero < 0 in + let implies a b = not a || b in + let a'' = + if implies max_pos (is_divisible ID.maximal) && implies min_neg (is_divisible ID.minimal) then + ID.meet a' (ID.sub (ID.mul (ID.div a b) b) c) + else a' + in + let a''' = + (* if both b and c are definite, we can get a precise value in the congruence domain *) + if ID.is_int b && ID.is_int c then + (* a%b == c -> a: c+bℤ *) + let t = ID.of_congruence ikind ((BatOption.get @@ ID.to_int c), (BatOption.get @@ ID.to_int b)) in + ID.meet a'' t + else a'' + in + meet_bin a''' b' + | Eq | Ne as op -> + let both x = x, x in + let m = ID.meet a b in + (match op, ID.to_bool c with + | Eq, Some true + | Ne, Some false -> both m (* def. equal: if they compare equal, both values must be from the meet *) + | Eq, Some false + | Ne, Some true -> (* def. unequal *) + (* Both values can not be in the meet together, but it's not sound to exclude the meet from both. + * e.g. a=[0,1], b=[1,2], meet a b = [1,1], but (a != b) does not imply a=[0,0], b=[2,2] since others are possible: a=[1,1], b=[2,2] + * Only if a is a definite value, we can exclude it from b: *) + let excl a b = match ID.to_int a with Some x -> ID.of_excl_list ikind [x] | None -> b in + let a' = excl b a in + let b' = excl a b in + if M.tracing then M.tracel "inv" "inv_bin_int: unequal: %a and %a; ikind: %a; a': %a, b': %a\n" ID.pretty a ID.pretty b d_ikind ikind ID.pretty a' ID.pretty b'; + meet_bin a' b' + | _, _ -> a, b + ) + | Lt | Le | Ge | Gt as op -> + let pred x = BI.sub x BI.one in + let succ x = BI.add x BI.one in + (match ID.minimal a, ID.maximal a, ID.minimal b, ID.maximal b with + | Some l1, Some u1, Some l2, Some u2 -> + (* if M.tracing then M.tracel "inv" "Op: %s, l1: %Ld, u1: %Ld, l2: %Ld, u2: %Ld\n" (show_binop op) l1 u1 l2 u2; *) + (match op, ID.to_bool c with + | Le, Some true + | Gt, Some false -> meet_bin (ID.ending ikind u2) (ID.starting ikind l1) + | Ge, Some true + | Lt, Some false -> meet_bin (ID.starting ikind l2) (ID.ending ikind u1) + | Lt, Some true + | Ge, Some false -> meet_bin (ID.ending ikind (pred u2)) (ID.starting ikind (succ l1)) + | Gt, Some true + | Le, Some false -> meet_bin (ID.starting ikind (succ l2)) (ID.ending ikind (pred u1)) + | _, _ -> a, b) + | _ -> a, b) + | BOr | BXor as op-> + if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; + (* Be careful: inv_exp performs a meet on both arguments of the BOr / BXor. *) + a, b + | LAnd -> + if ID.to_bool c = Some true then + meet_bin c c + else + a, b + | op -> + if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; + a, b + in + let inv_bin_float (a, b) c op = + let open Stdlib in + let meet_bin a' b' = FD.meet a a', FD.meet b b' in + (* Refining the abstract values based on branching is roughly based on the idea in [Symbolic execution of floating-point computations](https://hal.inria.fr/inria-00540299/document) + However, their approach is only applicable to the "nearest" rounding mode. Here we use a more general approach covering all possible rounding modes and therefore + use the actual `pred c_min`/`succ c_max` for the outer-bounds instead of the middles between `c_min` and `pred c_min`/`c_max` and `succ c_max` as suggested in the paper. + This also removes the necessity of computing those expressions with higher precise than in the concrete. + *) + try + match op with + | PlusA -> + (* A + B = C, \forall a \in A. a + b_min > pred c_min \land a + b_max < succ c_max + \land a + b_max > pred c_min \land a + b_min < succ c_max + \rightarrow A = [min(pred c_min - b_min, pred c_min - b_max), max(succ c_max - b_max, succ c_max - b_min)] + \rightarrow A = [pred c_min - b_max, succ c_max - b_min] + *) + let reverse_add v v' = (match FD.minimal c, FD.maximal c, FD.minimal v, FD.maximal v with + | Some c_min, Some c_max, Some v_min, Some v_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> + let l = Float.pred c_min -. v_max in + let h = Float.succ c_max -. v_min in + FD.of_interval (FD.get_fkind c) (l, h) + | _ -> v') in + meet_bin (reverse_add b a) (reverse_add a b) + | MinusA -> + (* A - B = C \ forall a \in A. a - b_max > pred c_min \land a - b_min < succ c_max + \land a - b_min > pred c_min \land a - b_max < succ c_max + \rightarrow A = [min(pred c_min + b_max, pred c_min + b_min), max(succ c_max + b_max, succ c_max + b_max)] + \rightarrow A = [pred c_min + b_min, succ c_max + b_max] + *) + let a' = (match FD.minimal c, FD.maximal c, FD.minimal b, FD.maximal b with + | Some c_min, Some c_max, Some b_min, Some b_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> + let l = Float.pred c_min +. b_min in + let h = Float.succ c_max +. b_max in + FD.of_interval (FD.get_fkind c) (l, h) + | _ -> a) in + (* A - B = C \ forall b \in B. a_min - b > pred c_min \land a_max - b < succ c_max + \land a_max - b > pred c_min \land a_min - b < succ c_max + \rightarrow B = [min(a_max - succ c_max, a_min - succ c_max), max(a_min - pred c_min, a_max - pred c_min)] + \rightarrow B = [a_min - succ c_max, a_max - pred c_min] + *) + let b' = (match FD.minimal c, FD.maximal c, FD.minimal a, FD.maximal a with + | Some c_min, Some c_max, Some a_min, Some a_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> + let l = a_min -. Float.succ c_max in + let h = a_max -. Float.pred c_min in + FD.of_interval (FD.get_fkind c) (l, h) + | _ -> b) in + meet_bin a' b' + | Mult -> + (* A * B = C \forall a \in A, a > 0. a * b_min > pred c_min \land a * b_max < succ c_max + A * B = C \forall a \in A, a < 0. a * b_max > pred c_min \land a * b_min < succ c_max + (with negative b reversed <>) + \rightarrow A = [min(pred c_min / b_min, pred c_min / b_max, succ c_max / b_min, succ c_max /b_max), + max(succ c_max / b_min, succ c_max /b_max, pred c_min / b_min, pred c_min / b_max)] + *) + let reverse_mul v v' = (match FD.minimal c, FD.maximal c, FD.minimal v, FD.maximal v with + | Some c_min, Some c_max, Some v_min, Some v_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> + let v1, v2, v3, v4 = (Float.pred c_min /. v_min), (Float.pred c_min /. v_max), (Float.succ c_max /. v_min), (Float.succ c_max /. v_max) in + let l = Float.min (Float.min v1 v2) (Float.min v3 v4) in + let h = Float.max (Float.max v1 v2) (Float.max v3 v4) in + FD.of_interval (FD.get_fkind c) (l, h) + | _ -> v') in + meet_bin (reverse_mul b a) (reverse_mul a b) + | Div -> + (* A / B = C \forall a \in A, a > 0, b_min > 1. a / b_max > pred c_min \land a / b_min < succ c_max + A / B = C \forall a \in A, a < 0, b_min > 1. a / b_min > pred c_min \land a / b_max < succ c_max + A / B = C \forall a \in A, a > 0, 0 < b_min, b_max < 1. a / b_max > pred c_min \land a / b_min < succ c_max + A / B = C \forall a \in A, a < 0, 0 < b_min, b_max < 1. a / b_min > pred c_min \land a / b_max < succ c_max + ... same for negative b + \rightarrow A = [min(b_max * pred c_min, b_min * pred c_min, b_min * succ c_max, b_max * succ c_max), + max(b_max * succ c_max, b_min * succ c_max, b_max * pred c_min, b_min * pred c_min)] + *) + let a' = (match FD.minimal c, FD.maximal c, FD.minimal b, FD.maximal b with + | Some c_min, Some c_max, Some b_min, Some b_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> + let v1, v2, v3, v4 = (Float.pred c_min *. b_max), (Float.pred c_min *. b_min), (Float.succ c_max *. b_max), (Float.succ c_max *. b_min) in + let l = Float.min (Float.min v1 v2) (Float.min v3 v4) in + let h = Float.max (Float.max v1 v2) (Float.max v3 v4) in + FD.of_interval (FD.get_fkind c) (l, h) + | _ -> a) in + (* A / B = C \forall b \in B, b > 0, a_min / b > pred c_min \land a_min / b < succ c_max + \land a_max / b > pred c_min \land a_max / b < succ c_max + A / B = C \forall b \in B, b < 0, a_min / b > pred c_min \land a_min / b < succ c_max + \land a_max / b > pred c_min \land a_max / b < succ c_max + \rightarrow (b != 0) B = [min(a_min / succ c_max, a_max / succ c_max, a_min / pred c_min, a_max / pred c_min), + max(a_min / pred c_min, a_max / pred c_min, a_min / succ c_max, a_max / succ c_max)] + *) + let b' = (match FD.minimal c, FD.maximal c, FD.minimal a, FD.maximal a with + | Some c_min, Some c_max, Some a_min, Some a_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> + let v1, v2, v3, v4 = (a_min /. Float.pred c_min), (a_max /. Float.pred c_min), (a_min /. Float.succ c_max), (a_max /. Float.succ c_max) in + let l = Float.min (Float.min v1 v2) (Float.min v3 v4) in + let h = Float.max (Float.max v1 v2) (Float.max v3 v4) in + FD.of_interval (FD.get_fkind c) (l, h) + | _ -> b) in + meet_bin a' b' + | Eq | Ne as op -> + let both x = x, x in + (match op, ID.to_bool (FD.to_int IBool c) with + | Eq, Some true + | Ne, Some false -> both (FD.meet a b) (* def. equal: if they compare equal, both values must be from the meet *) + | Eq, Some false + | Ne, Some true -> (* def. unequal *) + (* M.debug ~category:Analyzer "Can't use unequal information about float value in expression \"%a\"." d_plainexp exp; *) + a, b + | _, _ -> a, b + ) + | Lt | Le | Ge | Gt as op -> + (match FD.minimal a, FD.maximal a, FD.minimal b, FD.maximal b with + | Some l1, Some u1, Some l2, Some u2 -> + (match op, ID.to_bool (FD.to_int IBool c) with + | Le, Some true + | Gt, Some false -> meet_bin (FD.ending (FD.get_fkind a) u2) (FD.starting (FD.get_fkind b) l1) + | Ge, Some true + | Lt, Some false -> meet_bin (FD.starting (FD.get_fkind a) l2) (FD.ending (FD.get_fkind b) u1) + | Lt, Some true + | Ge, Some false -> meet_bin (FD.ending_before (FD.get_fkind a) u2) (FD.starting_after (FD.get_fkind b) l1) + | Gt, Some true + | Le, Some false -> meet_bin (FD.starting_after (FD.get_fkind a) l2) (FD.ending_before (FD.get_fkind b) u1) + | _, _ -> a, b) + | _ -> a, b) + | op -> + if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; + a, b + with FloatDomain.ArithmeticOnFloatBot _ -> raise Analyses.Deadcode + in + let eval e st = eval_rv a gs st e in + let eval_bool e st = match eval e st with `Int i -> ID.to_bool i | _ -> None in + let rec inv_exp c_typed exp (st:D.t): D.t = + (* trying to improve variables in an expression so it is bottom means dead code *) + if VD.is_bot_value c_typed then raise Analyses.Deadcode; + match exp, c_typed with + | UnOp (LNot, e, _), `Int c -> + let ikind = Cilfacade.get_ikind_exp e in + let c' = + match ID.to_bool (unop_ID LNot c) with + | Some true -> + (* i.e. e should evaluate to [1,1] *) + (* LNot x is 0 for any x != 0 *) + ID.of_excl_list ikind [BI.zero] + | Some false -> ID.of_bool ikind false + | _ -> ID.top_of ikind + in + inv_exp (`Int c') e st + | UnOp (Neg, e, _), `Float c -> inv_exp (`Float (unop_FD Neg c)) e st + | UnOp ((BNot|Neg) as op, e, _), `Int c -> inv_exp (`Int (unop_ID op c)) e st + (* no equivalent for `Float, as VD.is_safe_cast fails for all float types anyways *) + | BinOp(op, CastE (t1, c1), CastE (t2, c2), t), `Int c when (op = Eq || op = Ne) && typeSig (Cilfacade.typeOf c1) = typeSig (Cilfacade.typeOf c2) && VD.is_safe_cast t1 (Cilfacade.typeOf c1) && VD.is_safe_cast t2 (Cilfacade.typeOf c2) -> + inv_exp (`Int c) (BinOp (op, c1, c2, t)) st + | (BinOp (op, e1, e2, _) as e, `Float _) + | (BinOp (op, e1, e2, _) as e, `Int _) -> + let invert_binary_op c pretty c_int c_float = + if M.tracing then M.tracel "inv" "binop %a with %a %a %a == %a\n" d_exp e VD.pretty (eval e1 st) d_binop op VD.pretty (eval e2 st) pretty c; + (match eval e1 st, eval e2 st with + | `Int a, `Int b -> + let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) + let a', b' = inv_bin_int (a, b) ikind (c_int ikind) op in + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; + let st' = inv_exp (`Int a') e1 st in + let st'' = inv_exp (`Int b') e2 st' in + st'' + | `Float a, `Float b -> + let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) + let a', b' = inv_bin_float (a, b) (c_float fkind) op in + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; + let st' = inv_exp (`Float a') e1 st in + let st'' = inv_exp (`Float b') e2 st' in + st'' + (* Mixed `Float and `Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) + | `Int _, `Float _ | `Float _, `Int _ -> failwith "ill-typed program"; + (* | `Address a, `Address b -> ... *) + | a1, a2 -> fallback ("binop: got abstract values that are not `Int: " ^ sprint VD.pretty a1 ^ " and " ^ sprint VD.pretty a2) st) + (* use closures to avoid unused casts *) + in (match c_typed with + | `Int c -> invert_binary_op c ID.pretty (fun ik -> ID.cast_to ik c) (fun fk -> FD.of_int fk c) + | `Float c -> invert_binary_op c FD.pretty (fun ik -> FD.to_int ik c) (fun fk -> FD.cast_to fk c) + | _ -> failwith "unreachable") + | Lval x, `Int _(* meet x with c *) + | Lval x, `Float _ -> (* meet x with c *) + let update_lval c x c' pretty = refine_lv ctx a gs st c x c' pretty exp in + let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) + (match c_typed with + | `Int c -> update_lval c x (match t with + | TPtr _ -> `Address (AD.of_int (module ID) c) + | TInt (ik, _) + | TEnum ({ekind = ik; _}, _) -> `Int (ID.cast_to ik c) + | TFloat (fk, _) -> `Float (FD.of_int fk c) + | _ -> `Int c) ID.pretty + | `Float c -> update_lval c x (match t with + (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) + | TInt (ik, _) -> `Int (FD.to_int ik c) + (* this is theoretically possible and should be handled correctly, however i can't imagine an actual piece of c code producing this?! *) + | TEnum ({ekind = ik; _}, _) -> `Int (FD.to_int ik c) + | TFloat (fk, _) -> `Float (FD.cast_to fk c) + | _ -> `Float c) FD.pretty + | _ -> failwith "unreachable") + | Const _ , _ -> st (* nothing to do *) + | CastE ((TFloat (_, _)), e), `Float c -> + (match Cilfacade.typeOf e, FD.get_fkind c with + | TFloat (FLongDouble as fk, _), FFloat + | TFloat (FDouble as fk, _), FFloat + | TFloat (FLongDouble as fk, _), FDouble + | TFloat (fk, _), FLongDouble + | TFloat (FDouble as fk, _), FDouble + | TFloat (FFloat as fk, _), FFloat -> inv_exp (`Float (FD.cast_to fk c)) e st + | _ -> fallback ("CastE: incompatible types") st) + | CastE ((TInt (ik, _)) as t, e), `Int c + | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e), `Int c -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) + (match eval e st with + | `Int i -> + if ID.leq i (ID.cast_to ik i) then + match Cilfacade.typeOf e with + | TInt(ik_e, _) + | TEnum ({ekind = ik_e; _ }, _) -> + let c' = ID.cast_to ik_e c in + if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; + inv_exp (`Int c') e st + | x -> fallback ("CastE: e did evaluate to `Int, but the type did not match" ^ sprint d_type t) st + else + fallback ("CastE: " ^ sprint d_plainexp e ^ " evaluates to " ^ sprint ID.pretty i ^ " which is bigger than the type it is cast to which is " ^ sprint d_type t) st + | v -> fallback ("CastE: e did not evaluate to `Int, but " ^ sprint VD.pretty v) st) + | e, _ -> fallback (sprint d_plainexp e ^ " not implemented") st + in + if eval_bool exp st = Some (not tv) then raise Analyses.Deadcode (* we already know that the branch is dead *) + else + (* C11 6.5.13, 6.5.14, 6.5.3.3: LAnd, LOr and LNot also return 0 or 1 *) + let is_cmp = function + | UnOp (LNot, _, _) + | BinOp ((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr), _, _, _) -> true + | _ -> false + in + try + let ik = Cilfacade.get_ikind_exp exp in + let itv = if not tv || is_cmp exp then (* false is 0, but true can be anything that is not 0, except for comparisons which yield 1 *) + ID.of_bool ik tv (* this will give 1 for true which is only ok for comparisons *) + else + ID.of_excl_list ik [BI.zero] (* Lvals, Casts, arithmetic operations etc. should work with true = non_zero *) + in + inv_exp (`Int itv) exp st + with Invalid_argument _ -> + let fk = Cilfacade.get_fkind_exp exp in + let ftv = if not tv then (* false is 0, but true can be anything that is not 0, except for comparisons which yield 1 *) + FD.of_const fk 0. + else + FD.top_of fk + in + inv_exp (`Float ftv) exp st +end From 342c23f72df8911b36921331d24cbe2e4e39aadf Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 31 Aug 2022 14:40:05 +0300 Subject: [PATCH 027/132] Fix witness invariant lines in tauto and contra tests --- tests/regression/56-witness/17-base-unassume-tauto.yml | 2 +- tests/regression/56-witness/18-base-unassume-contra.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/56-witness/17-base-unassume-tauto.yml b/tests/regression/56-witness/17-base-unassume-tauto.yml index 5f5bca4f64..17843875fb 100644 --- a/tests/regression/56-witness/17-base-unassume-tauto.yml +++ b/tests/regression/56-witness/17-base-unassume-tauto.yml @@ -19,7 +19,7 @@ location: file_name: 17-base-unassume-tauto.c file_hash: a1dbab3d2dc60945ace118bcacd88e4d448ddb3aaa5e9aebe3d0872266256f89 - line: 8 + line: 7 column: 2 function: main loop_invariant: diff --git a/tests/regression/56-witness/18-base-unassume-contra.yml b/tests/regression/56-witness/18-base-unassume-contra.yml index 65f098fcc6..cd618e7ae4 100644 --- a/tests/regression/56-witness/18-base-unassume-contra.yml +++ b/tests/regression/56-witness/18-base-unassume-contra.yml @@ -19,7 +19,7 @@ location: file_name: 18-base-unassume-contra.c file_hash: e173c414639b0c08d00854dc6974f87ccf2ae29829c2e5542f984eb8a52929ce - line: 8 + line: 7 column: 2 function: main loop_invariant: From 2bef78063f27347a15ed13969e05bfda2eeb7550 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 31 Aug 2022 14:40:36 +0300 Subject: [PATCH 028/132] Hack BaseInvariant for base unassume --- src/analyses/base.ml | 92 ++++++++++++++++++- src/analyses/baseInvariant.ml | 14 ++- .../56-witness/16-base-unassume-dependent.c | 6 +- .../56-witness/17-base-unassume-tauto.c | 4 +- 4 files changed, 105 insertions(+), 11 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index e51092f37e..63e43f4afb 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1616,6 +1616,9 @@ struct if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; set' x v st ) + + let id_meet_down ~old ~c = ID.meet old c + let fd_meet_down ~old ~c = FD.meet old c end module Invariant = BaseInvariant.Make (InvariantEval) @@ -2358,9 +2361,96 @@ struct query (ctx' asked') q ) in + let octx = ctx in (* original ctx with non-top values *) let ctx = ctx' Queries.Set.empty in + (* TODO: deduplicate with invariant *) + let module UnassumeEval = + struct + module D = D + module V = V + module G = G + + let oa = Analyses.ask_of_ctx octx + let ost = octx.local + + (* all evals happen in octx with non-top values *) + + let eval_rv a gs st e = eval_rv oa gs ost e + let eval_rv_address a gs st e = eval_rv_address oa gs ost e + let eval_lv a gs st lv = eval_lv oa gs ost lv + + let apply_invariant oldv newv = + match oldv, newv with + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o && AD.mem (Addr.unknown_ptr ()) n -> *) + (* `Address (AD.join o n) *) + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o -> `Address n *) + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) n -> `Address o *) + | _ -> VD.meet oldv newv + + (* all updates happen in ctx with top values *) + + let refine_lv_fallback ctx a gs st lval value tv = + if M.tracing then M.tracec "invariant" "Restricting %a with %a\n" d_lval lval VD.pretty value; + let addr = eval_lv oa gs ost lval in + if (AD.is_top addr) then st + else + let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) + let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in + let t_lval = Cilfacade.typeOfLval lval in + let state_with_excluded = set a gs st addr t_lval value ~invariant:true ~ctx in + let value = get a gs state_with_excluded addr None in + let new_val = apply_invariant oldval value in + if M.tracing then M.traceu "invariant" "New value is %a\n" VD.pretty new_val; + (* make that address meet the invariant, i.e exclusion sets will be joined *) + if is_some_bot new_val then ( + if M.tracing then M.tracel "branch" "C The branch %B is dead!\n" tv; + raise Analyses.Deadcode + ) + else if VD.is_bot new_val + then set a gs st addr t_lval value ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) + else set a gs st addr t_lval new_val ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) + + let refine_lv ctx a gs st c x c' pretty exp = + let eval e st = eval_rv a gs st e in + let set' lval v st = set a gs st (eval_lv oa gs ost lval) (Cilfacade.typeOfLval lval) v ~invariant:true ~ctx in + match x with + | Var var, o -> + (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) + let oldv = get_var a gs st var in + let offs = convert_offset oa gs ost o in + let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in + let v = VD.meet oldv newv in + if is_some_bot v then raise Deadcode + else ( + if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v pretty c VD.pretty c'; + let r = set' (Var var,NoOffset) v st in + if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; + r + ) + | Mem _, _ -> + (* For accesses via pointers, not yet *) + let oldv = eval (Lval x) st in + let v = VD.meet oldv c' in + if is_some_bot v then raise Deadcode + else ( + if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; + set' x v st + ) + + (* don't meet with current octx values when propagating inverse operands down *) + let id_meet_down ~old ~c = c + let fd_meet_down ~old ~c = c + end + in + let module Unassume = BaseInvariant.Make (UnassumeEval) in if M.tracing then M.traceli "unassume" "base unassuming\n"; - let r = invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true in + let r = + try + Unassume.invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true + with Deadcode -> (* contradiction in unassume *) + D.bot () + in + (* TODO: unassume fixpoint *) if M.tracing then M.traceu "unassume" "base unassumed\n"; r in diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index ba75e3d0d2..39dedb925a 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -20,6 +20,9 @@ sig val refine_lv_fallback: (D.t, G.t, _, V.t) Analyses.ctx -> Queries.ask -> (V.t -> G.t) -> D.t -> lval -> VD.t -> bool -> D.t val refine_lv: (D.t, G.t, _, V.t) Analyses.ctx -> Queries.ask -> (V.t -> G.t) -> D.t -> 'a -> lval -> VD.t -> (unit -> 'a -> doc) -> exp -> D.t + + val id_meet_down: old:ID.t -> c:ID.t -> ID.t + val fd_meet_down: old:FD.t -> c:FD.t -> FD.t end module Make (Eval: Eval) = @@ -182,7 +185,7 @@ struct else x in - let meet_bin a' b' = ID.meet a a', ID.meet b b' in + let meet_bin a' b' = id_meet_down ~old:a ~c:a', id_meet_down ~old:b ~c:b' in let meet_com oi = (* commutative *) try meet_bin (oi c b) (oi c a) @@ -200,7 +203,7 @@ struct let refine_by x y = (match ID.to_int y with | None -> x | Some v when BI.equal (BI.rem v (BI.of_int 2)) BI.zero (* v % 2 = 0 *) -> x (* A refinement would still be possible here, but has to take non-injectivity into account. *) - | Some v (* when Int64.rem v 2L = 1L *) -> ID.meet x (ID.div c y)) (* Div is ok here, c must be divisible by a and b *) + | Some v (* when Int64.rem v 2L = 1L *) -> id_meet_down ~old:x ~c:(ID.div c y)) (* Div is ok here, c must be divisible by a and b *) in (refine_by a b, refine_by b a) | MinusA -> meet_non ID.add ID.sub @@ -301,7 +304,7 @@ struct in let inv_bin_float (a, b) c op = let open Stdlib in - let meet_bin a' b' = FD.meet a a', FD.meet b b' in + let meet_bin a' b' = fd_meet_down ~old:a ~c:a', fd_meet_down ~old:b ~c:b' in (* Refining the abstract values based on branching is roughly based on the idea in [Symbolic execution of floating-point computations](https://hal.inria.fr/inria-00540299/document) However, their approach is only applicable to the "nearest" rounding mode. Here we use a more general approach covering all possible rounding modes and therefore use the actual `pred c_min`/`succ c_max` for the outer-bounds instead of the middles between `c_min` and `pred c_min`/`c_max` and `succ c_max` as suggested in the paper. @@ -400,7 +403,7 @@ struct | Eq, Some false | Ne, Some true -> (* def. unequal *) (* M.debug ~category:Analyzer "Can't use unequal information about float value in expression \"%a\"." d_plainexp exp; *) - a, b + a, b (* TODO: no meet_bin? *) | _, _ -> a, b ) | Lt | Le | Ge | Gt as op -> @@ -510,7 +513,8 @@ struct match Cilfacade.typeOf e with | TInt(ik_e, _) | TEnum ({ekind = ik_e; _ }, _) -> - let c' = ID.cast_to ik_e c in + (* let c' = ID.cast_to ik_e c in *) + let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; inv_exp (`Int c') e st | x -> fallback ("CastE: e did evaluate to `Int, but the type did not match" ^ sprint d_type t) st diff --git a/tests/regression/56-witness/16-base-unassume-dependent.c b/tests/regression/56-witness/16-base-unassume-dependent.c index 42d1b08742..171cef4ef7 100644 --- a/tests/regression/56-witness/16-base-unassume-dependent.c +++ b/tests/regression/56-witness/16-base-unassume-dependent.c @@ -1,12 +1,12 @@ -// SKIP PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 16-base-unassume-dependent.yml --enable ana.int.interval +// PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 16-base-unassume-dependent.yml --enable ana.int.interval #include int main() { int i, j; i = 0; j = 42; - __goblint_check(i == 0); // TODO UNKNOWN (intentional by unassume) - __goblint_check(j == 42); // TODO UNKNOWN (intentional by unassume) + __goblint_check(i == 0); // UNKNOWN (intentional by unassume) + __goblint_check(j == 42); // UNKNOWN (intentional by unassume) __goblint_check(0 <= i); __goblint_check(i <= 42); __goblint_check(0 <= j); diff --git a/tests/regression/56-witness/17-base-unassume-tauto.c b/tests/regression/56-witness/17-base-unassume-tauto.c index b1516b43b4..cc64c6a153 100644 --- a/tests/regression/56-witness/17-base-unassume-tauto.c +++ b/tests/regression/56-witness/17-base-unassume-tauto.c @@ -1,9 +1,9 @@ -// SKIP PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 17-base-unassume-tauto.yml --enable ana.int.interval +// PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 17-base-unassume-tauto.yml --enable ana.int.interval #include int main() { int i; i = 0; - __goblint_check(i == 0); // TODO UNKNOWN (intentional by unassume) + __goblint_check(i == 0); // UNKNOWN (intentional by unassume) return 0; } \ No newline at end of file From 8f2ed730116ab00eeac33e7b5177389911036c83 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 31 Aug 2022 15:17:29 +0300 Subject: [PATCH 029/132] Use fixpoint for base unassume --- src/analyses/base.ml | 269 +++++++++--------- .../56-witness/17-base-unassume-tauto.c | 2 + 2 files changed, 143 insertions(+), 128 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 63e43f4afb..af3b71ac64 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2317,140 +2317,153 @@ struct in (* TODO: structural unassume instead of invariant hack *) let e_d = - (* The usual recursion trick for ctx. *) - (* Must change ctx used by ask to also use new st (not ctx.local), otherwise recursive EvalInt queries use outdated state. *) - (* Note: query is just called on base, but not any other analyses. Potentially imprecise, but seems to be sufficient for now. *) - let local: D.t = {ctx.local with cpa = e_cpa} in - let rec ctx' asked = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> query' asked q) - ; local - } - and query': type a. Queries.Set.t -> a Queries.t -> a Queries.result = fun asked q -> - let anyq = Queries.Any q in - if Queries.Set.mem anyq asked then - Queries.Result.top q (* query cycle *) - else ( - let asked' = Queries.Set.add anyq asked in - match q with - | MayEscape _ - | MayBePublic _ - | MayBePublicWithout _ - | MustBeProtectedBy _ - | MustLockset - | MustBeAtomic - | MustBeSingleThreaded - | MustBeUniqueThread - | CurrentThreadId - | MayBeThreadReturn - | PartAccess _ - | IsHeapVar _ - | IsMultiple _ - | CreatedThreads - | MustJoinedThreads -> - (* These queries are safe to ask from outside, - where base doesn't have the partial top local state. - They are also needed for sensible eval behavior via [inv_exp] - such that everything wouldn't be may escaped. *) - ctx.ask q - | _ -> - (* Other queries are not safe, because they would - query the local value state instead of top. - Therefore, these are answered only by base on the - partial top local state. *) - query (ctx' asked') q - ) - in - let octx = ctx in (* original ctx with non-top values *) - let ctx = ctx' Queries.Set.empty in - (* TODO: deduplicate with invariant *) - let module UnassumeEval = - struct - module D = D - module V = V - module G = G - - let oa = Analyses.ask_of_ctx octx - let ost = octx.local - - (* all evals happen in octx with non-top values *) - - let eval_rv a gs st e = eval_rv oa gs ost e - let eval_rv_address a gs st e = eval_rv_address oa gs ost e - let eval_lv a gs st lv = eval_lv oa gs ost lv - - let apply_invariant oldv newv = - match oldv, newv with - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o && AD.mem (Addr.unknown_ptr ()) n -> *) - (* `Address (AD.join o n) *) - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o -> `Address n *) - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) n -> `Address o *) - | _ -> VD.meet oldv newv - - (* all updates happen in ctx with top values *) - - let refine_lv_fallback ctx a gs st lval value tv = - if M.tracing then M.tracec "invariant" "Restricting %a with %a\n" d_lval lval VD.pretty value; - let addr = eval_lv oa gs ost lval in - if (AD.is_top addr) then st - else - let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) - let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in - let t_lval = Cilfacade.typeOfLval lval in - let state_with_excluded = set a gs st addr t_lval value ~invariant:true ~ctx in - let value = get a gs state_with_excluded addr None in - let new_val = apply_invariant oldval value in - if M.tracing then M.traceu "invariant" "New value is %a\n" VD.pretty new_val; - (* make that address meet the invariant, i.e exclusion sets will be joined *) - if is_some_bot new_val then ( - if M.tracing then M.tracel "branch" "C The branch %B is dead!\n" tv; - raise Analyses.Deadcode - ) - else if VD.is_bot new_val - then set a gs st addr t_lval value ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) - else set a gs st addr t_lval new_val ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) - - let refine_lv ctx a gs st c x c' pretty exp = - let eval e st = eval_rv a gs st e in - let set' lval v st = set a gs st (eval_lv oa gs ost lval) (Cilfacade.typeOfLval lval) v ~invariant:true ~ctx in - match x with - | Var var, o -> - (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) - let oldv = get_var a gs st var in - let offs = convert_offset oa gs ost o in - let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in - let v = VD.meet oldv newv in - if is_some_bot v then raise Deadcode - else ( - if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v pretty c VD.pretty c'; - let r = set' (Var var,NoOffset) v st in - if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; - r - ) - | Mem _, _ -> - (* For accesses via pointers, not yet *) - let oldv = eval (Lval x) st in - let v = VD.meet oldv c' in - if is_some_bot v then raise Deadcode - else ( - if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; - set' x v st - ) - - (* don't meet with current octx values when propagating inverse operands down *) - let id_meet_down ~old ~c = c - let fd_meet_down ~old ~c = c - end + let ctx_with_local local = + (* The usual recursion trick for ctx. *) + (* Must change ctx used by ask to also use new st (not ctx.local), otherwise recursive EvalInt queries use outdated state. *) + (* Note: query is just called on base, but not any other analyses. Potentially imprecise, but seems to be sufficient for now. *) + let rec ctx' asked = + { ctx with + ask = (fun (type a) (q: a Queries.t) -> query' asked q) + ; local + } + and query': type a. Queries.Set.t -> a Queries.t -> a Queries.result = fun asked q -> + let anyq = Queries.Any q in + if Queries.Set.mem anyq asked then + Queries.Result.top q (* query cycle *) + else ( + let asked' = Queries.Set.add anyq asked in + match q with + | MayEscape _ + | MayBePublic _ + | MayBePublicWithout _ + | MustBeProtectedBy _ + | MustLockset + | MustBeAtomic + | MustBeSingleThreaded + | MustBeUniqueThread + | CurrentThreadId + | MayBeThreadReturn + | PartAccess _ + | IsHeapVar _ + | IsMultiple _ + | CreatedThreads + | MustJoinedThreads -> + (* These queries are safe to ask from outside, + where base doesn't have the partial top local state. + They are also needed for sensible eval behavior via [inv_exp] + such that everything wouldn't be may escaped. *) + ctx.ask q + | _ -> + (* Other queries are not safe, because they would + query the local value state instead of top. + Therefore, these are answered only by base on the + partial top local state. *) + query (ctx' asked') q + ) + in + ctx' Queries.Set.empty in - let module Unassume = BaseInvariant.Make (UnassumeEval) in - if M.tracing then M.traceli "unassume" "base unassuming\n"; - let r = + let f st = + let local: D.t = {ctx.local with cpa = e_cpa} in + let octx = ctx_with_local st in (* original ctx with non-top values *) + (* TODO: deduplicate with invariant *) + let ctx = ctx_with_local local in + let module UnassumeEval = + struct + module D = D + module V = V + module G = G + + let oa = Analyses.ask_of_ctx octx + let ost = octx.local + + (* all evals happen in octx with non-top values *) + + let eval_rv a gs st e = eval_rv oa gs ost e + let eval_rv_address a gs st e = eval_rv_address oa gs ost e + let eval_lv a gs st lv = eval_lv oa gs ost lv + + let apply_invariant oldv newv = + match oldv, newv with + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o && AD.mem (Addr.unknown_ptr ()) n -> *) + (* `Address (AD.join o n) *) + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o -> `Address n *) + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) n -> `Address o *) + | _ -> VD.meet oldv newv + + (* all updates happen in ctx with top values *) + + let refine_lv_fallback ctx a gs st lval value tv = + if M.tracing then M.tracec "invariant" "Restricting %a with %a\n" d_lval lval VD.pretty value; + let addr = eval_lv oa gs ost lval in + if (AD.is_top addr) then st + else + let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) + let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in + let t_lval = Cilfacade.typeOfLval lval in + let state_with_excluded = set a gs st addr t_lval value ~invariant:true ~ctx in + let value = get a gs state_with_excluded addr None in + let new_val = apply_invariant oldval value in + if M.tracing then M.traceu "invariant" "New value is %a\n" VD.pretty new_val; + (* make that address meet the invariant, i.e exclusion sets will be joined *) + if is_some_bot new_val then ( + if M.tracing then M.tracel "branch" "C The branch %B is dead!\n" tv; + raise Analyses.Deadcode + ) + else if VD.is_bot new_val + then set a gs st addr t_lval value ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) + else set a gs st addr t_lval new_val ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) + + let refine_lv ctx a gs st c x c' pretty exp = + let eval e st = eval_rv a gs st e in + let set' lval v st = set a gs st (eval_lv oa gs ost lval) (Cilfacade.typeOfLval lval) v ~invariant:true ~ctx in + match x with + | Var var, o -> + (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) + let oldv = get_var a gs st var in + let offs = convert_offset oa gs ost o in + let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in + let v = VD.meet oldv newv in + if is_some_bot v then raise Deadcode + else ( + if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v pretty c VD.pretty c'; + let r = set' (Var var,NoOffset) v st in + if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; + r + ) + | Mem _, _ -> + (* For accesses via pointers, not yet *) + let oldv = eval (Lval x) st in + let v = VD.meet oldv c' in + if is_some_bot v then raise Deadcode + else ( + if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; + set' x v st + ) + + (* don't meet with current octx values when propagating inverse operands down *) + let id_meet_down ~old ~c = c + let fd_meet_down ~old ~c = c + end + in + let module Unassume = BaseInvariant.Make (UnassumeEval) in try Unassume.invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true with Deadcode -> (* contradiction in unassume *) D.bot () in - (* TODO: unassume fixpoint *) + (* local upwards fixpoint with widening *) + (* TODO: narrowing phase *) + let rec lfp st = + let st' = f st in + let st'' = D.widen st (D.join st st') in + if D.equal st st'' then + st + else + lfp st'' + in + if M.tracing then M.traceli "unassume" "base unassuming\n"; + let r = lfp ctx.local in if M.tracing then M.traceu "unassume" "base unassumed\n"; r in diff --git a/tests/regression/56-witness/17-base-unassume-tauto.c b/tests/regression/56-witness/17-base-unassume-tauto.c index cc64c6a153..5b7be9828b 100644 --- a/tests/regression/56-witness/17-base-unassume-tauto.c +++ b/tests/regression/56-witness/17-base-unassume-tauto.c @@ -5,5 +5,7 @@ int main() { int i; i = 0; __goblint_check(i == 0); // UNKNOWN (intentional by unassume) + __goblint_check(i <= 1); // UNKNOWN (intentional by unassume) + __goblint_check(-1 <= i); // UNKNOWN (intentional by unassume) return 0; } \ No newline at end of file From 5fd25c9d087fea97e9ba91eb1b3019301927d7f2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 31 Aug 2022 15:42:25 +0300 Subject: [PATCH 030/132] Add narrowing to base unassume --- src/analyses/base.ml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index af3b71ac64..339100adaa 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2452,15 +2452,27 @@ struct with Deadcode -> (* contradiction in unassume *) D.bot () in - (* local upwards fixpoint with widening *) - (* TODO: narrowing phase *) + (* local fixpoint with widening and narrowing *) let rec lfp st = + if M.tracing then M.tracel "unassume" "base unassuming lfp st: %a\n" D.pretty st; let st' = f st in + if M.tracing then M.tracel "unassume" "base unassuming lfp st': %a\n" D.pretty st'; let st'' = D.widen st (D.join st st') in + if M.tracing then M.tracel "unassume" "base unassuming lfp st'': %a\n" D.pretty st''; if D.equal st st'' then - st + lfp' st else lfp st'' + and lfp' st = + if M.tracing then M.tracel "unassume" "base unassuming lfp' st: %a\n" D.pretty st; + let st' = f st in + if M.tracing then M.tracel "unassume" "base unassuming lfp' st': %a\n" D.pretty st'; + let st'' = D.narrow st st' in + if M.tracing then M.tracel "unassume" "base unassuming lfp' st'': %a\n" D.pretty st''; + if D.equal st st'' then + st + else + lfp' st'' in if M.tracing then M.traceli "unassume" "base unassuming\n"; let r = lfp ctx.local in From b00b2de86633547d5aed7b428b9c334b6e1950bd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 1 Sep 2022 11:10:24 +0300 Subject: [PATCH 031/132] Move base unassume soundness join into fixpoint function --- src/analyses/base.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 339100adaa..805b562f87 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2452,6 +2452,7 @@ struct with Deadcode -> (* contradiction in unassume *) D.bot () in + let f local = D.join ctx.local (f local) in (* join ctx.local to remain sound *) (* local fixpoint with widening and narrowing *) let rec lfp st = if M.tracing then M.tracel "unassume" "base unassuming lfp st: %a\n" D.pretty st; @@ -2475,13 +2476,13 @@ struct lfp' st'' in if M.tracing then M.traceli "unassume" "base unassuming\n"; - let r = lfp ctx.local in + let r = lfp ctx.local in (* start from ctx.local instead of D.bot () to avoid invariant on bot *) if M.tracing then M.traceu "unassume" "base unassumed\n"; r in M.info ~category:Witness "base unassumed invariant: %a" d_exp e; M.debug ~category:Witness "base unassumed state: %a" D.pretty e_d; - D.join ctx.local e_d + e_d (* ctx.local is joined in above *) ) else ( M.info ~category:Witness "base didn't unassume invariant: %a" d_exp e; From ed336aa6c18d6878f8686fd0dbb4186ef023c592 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 1 Sep 2022 11:18:07 +0300 Subject: [PATCH 032/132] Extract LocalFixpoint module --- src/analyses/base.ml | 25 ++----------------------- src/solvers/localFixpoint.ml | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 src/solvers/localFixpoint.ml diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 805b562f87..f96393f705 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2453,30 +2453,9 @@ struct D.bot () in let f local = D.join ctx.local (f local) in (* join ctx.local to remain sound *) - (* local fixpoint with widening and narrowing *) - let rec lfp st = - if M.tracing then M.tracel "unassume" "base unassuming lfp st: %a\n" D.pretty st; - let st' = f st in - if M.tracing then M.tracel "unassume" "base unassuming lfp st': %a\n" D.pretty st'; - let st'' = D.widen st (D.join st st') in - if M.tracing then M.tracel "unassume" "base unassuming lfp st'': %a\n" D.pretty st''; - if D.equal st st'' then - lfp' st - else - lfp st'' - and lfp' st = - if M.tracing then M.tracel "unassume" "base unassuming lfp' st: %a\n" D.pretty st; - let st' = f st in - if M.tracing then M.tracel "unassume" "base unassuming lfp' st': %a\n" D.pretty st'; - let st'' = D.narrow st st' in - if M.tracing then M.tracel "unassume" "base unassuming lfp' st'': %a\n" D.pretty st''; - if D.equal st st'' then - st - else - lfp' st'' - in + let module DFP = LocalFixpoint.Make (D) in if M.tracing then M.traceli "unassume" "base unassuming\n"; - let r = lfp ctx.local in (* start from ctx.local instead of D.bot () to avoid invariant on bot *) + let r = DFP.lfp ~init:ctx.local f in (* start from ctx.local instead of D.bot () to avoid invariant on bot *) if M.tracing then M.traceu "unassume" "base unassumed\n"; r in diff --git a/src/solvers/localFixpoint.ml b/src/solvers/localFixpoint.ml new file mode 100644 index 0000000000..03ea9462e8 --- /dev/null +++ b/src/solvers/localFixpoint.ml @@ -0,0 +1,22 @@ +(** Local fixpoint iteration solvers that don't use a constraint system. *) + +module Make (D: Lattice.S) = +struct + let lfp ?(init=D.bot ()) (f: D.t -> D.t): D.t = + let rec widening x = + let x' = f x in + let x'' = D.widen x (D.join x x') in + if D.equal x x'' then + narrowing x (* switch to narrowing phase *) + else + widening x'' + and narrowing x = + let x' = f x in + let x'' = D.narrow x x' in + if D.equal x x'' then + x (* end iteration *) + else + narrowing x'' + in + widening init +end From be4a4959159ce8b6e055e6539aefa76e0c170fdf Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 1 Sep 2022 12:00:55 +0300 Subject: [PATCH 033/132] Use empty environment in base unassume --- src/analyses/base.ml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f96393f705..ab9d2ab262 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2311,10 +2311,11 @@ struct let e_cpa = CPA.bot () in let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal in if List.for_all (fun v -> not v.vglob) vars then ( - let e_cpa = List.fold_left (fun e_cpa v -> + (* TODO: start with empty vars because unassume may unassume values for pointed variables not in the invariant exp *) + (* let e_cpa = List.fold_left (fun e_cpa v -> CPA.add v (VD.top_value v.vtype) e_cpa ) e_cpa vars - in + in *) (* TODO: structural unassume instead of invariant hack *) let e_d = let ctx_with_local local = @@ -2399,9 +2400,10 @@ struct if (AD.is_top addr) then st else let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) - let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in let t_lval = Cilfacade.typeOfLval lval in - let state_with_excluded = set a gs st addr t_lval value ~invariant:true ~ctx in + let oldval = if VD.is_bot oldval then VD.top_value t_lval else oldval in + let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in + let state_with_excluded = set a gs st addr t_lval value ~invariant:false ~ctx in (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) let value = get a gs state_with_excluded addr None in let new_val = apply_invariant oldval value in if M.tracing then M.traceu "invariant" "New value is %a\n" VD.pretty new_val; @@ -2411,16 +2413,18 @@ struct raise Analyses.Deadcode ) else if VD.is_bot new_val - then set a gs st addr t_lval value ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) - else set a gs st addr t_lval new_val ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) + (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) + then set a gs st addr t_lval value ~invariant:false ~ctx (* no *_raw because this is not a real assignment *) + else set a gs st addr t_lval new_val ~invariant:false ~ctx (* no *_raw because this is not a real assignment *) let refine_lv ctx a gs st c x c' pretty exp = let eval e st = eval_rv a gs st e in - let set' lval v st = set a gs st (eval_lv oa gs ost lval) (Cilfacade.typeOfLval lval) v ~invariant:true ~ctx in + let set' lval v st = set a gs st (eval_lv oa gs ost lval) (Cilfacade.typeOfLval lval) v ~invariant:false ~ctx in (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) match x with | Var var, o -> (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) let oldv = get_var a gs st var in + let oldv = if VD.is_bot oldv then VD.top_value var.vtype else oldv in let offs = convert_offset oa gs ost o in let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in let v = VD.meet oldv newv in From 2d8c4f3943f3f7617605c132d0abce29ec623a1d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 1 Sep 2022 13:33:17 +0300 Subject: [PATCH 034/132] Add base unassume test with dereferenced invariant --- .../56-witness/19-base-unassume-mem.c | 19 +++++++++++++ .../56-witness/19-base-unassume-mem.yml | 27 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/regression/56-witness/19-base-unassume-mem.c create mode 100644 tests/regression/56-witness/19-base-unassume-mem.yml diff --git a/tests/regression/56-witness/19-base-unassume-mem.c b/tests/regression/56-witness/19-base-unassume-mem.c new file mode 100644 index 0000000000..7ffad48c56 --- /dev/null +++ b/tests/regression/56-witness/19-base-unassume-mem.c @@ -0,0 +1,19 @@ +// PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 19-base-unassume-mem.yml --enable ana.int.interval +#include + +int main() { + int i, j; + int *p; + int r; // rand + i = 0; + j = 0; + if (r) + p = &i; + else + p = &j; + __goblint_check(i == 0); // UNKNOWN (intentional by unassume) + __goblint_check(j == 0); // UNKNOWN (intentional by unassume) + __goblint_check(i >= 0); + __goblint_check(j >= 0); + return 0; +} \ No newline at end of file diff --git a/tests/regression/56-witness/19-base-unassume-mem.yml b/tests/regression/56-witness/19-base-unassume-mem.yml new file mode 100644 index 0000000000..06a1758d68 --- /dev/null +++ b/tests/regression/56-witness/19-base-unassume-mem.yml @@ -0,0 +1,27 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: ddb9942d-998c-425d-a74f-228044bc38fb + creation_time: 2022-09-01T10:15:07Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-base-0-ged336aa6c-dirty + command_line: '''../../../goblint'' ''--enable'' ''ana.int.interval'' ''--enable'' + ''witness.yaml.enabled'' ''19-base-unassume-mem.c''' + task: + input_files: + - 19-base-unassume-mem.c + input_file_hashes: + 19-base-unassume-mem.c: d71d7065e36b8e0a2c42c96e5f6ea057d28065adae2d85b516989b8d43e77c91 + data_model: LP64 + language: C + location: + file_name: 19-base-unassume-mem.c + file_hash: d71d7065e36b8e0a2c42c96e5f6ea057d28065adae2d85b516989b8d43e77c91 + line: 14 + column: 2 + function: main + loop_invariant: + string: '*p >= 0' + type: assertion + format: C From 0c307d18cac7559b7f1d86e2dea2ff43a3f7e94d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 1 Sep 2022 13:33:38 +0300 Subject: [PATCH 035/132] Fix evals in base unassume mistakenly being used by refines --- src/analyses/base.ml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index ab9d2ab262..9d1b319b6e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2378,12 +2378,6 @@ struct let oa = Analyses.ask_of_ctx octx let ost = octx.local - (* all evals happen in octx with non-top values *) - - let eval_rv a gs st e = eval_rv oa gs ost e - let eval_rv_address a gs st e = eval_rv_address oa gs ost e - let eval_lv a gs st lv = eval_lv oa gs ost lv - let apply_invariant oldv newv = match oldv, newv with (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o && AD.mem (Addr.unknown_ptr ()) n -> *) @@ -2437,7 +2431,7 @@ struct ) | Mem _, _ -> (* For accesses via pointers, not yet *) - let oldv = eval (Lval x) st in + let oldv = eval (Lval x) st in (* TODO: this lval should be evaluated in octx, but its value in ctx *) let v = VD.meet oldv c' in if is_some_bot v then raise Deadcode else ( @@ -2445,6 +2439,13 @@ struct set' x v st ) + (* all evals happen in octx with non-top values *) + (* must be down here to make evals in refines call the real ones *) + + let eval_rv a gs st e = eval_rv oa gs ost e + let eval_rv_address a gs st e = eval_rv_address oa gs ost e + let eval_lv a gs st lv = eval_lv oa gs ost lv + (* don't meet with current octx values when propagating inverse operands down *) let id_meet_down ~old ~c = c let fd_meet_down ~old ~c = c From 29838872c59e41d01296fbd06f75c7368f339223 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 1 Sep 2022 16:02:09 +0300 Subject: [PATCH 036/132] Fix base unassume with multiple same dereferenced variables in same invariant --- src/analyses/base.ml | 64 ++++++++++++++++++- .../56-witness/19-base-unassume-mem.c | 2 + .../56-witness/19-base-unassume-mem.yml | 27 ++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 9d1b319b6e..c3ba6d3f90 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2412,7 +2412,66 @@ struct else set a gs st addr t_lval new_val ~invariant:false ~ctx (* no *_raw because this is not a real assignment *) let refine_lv ctx a gs st c x c' pretty exp = - let eval e st = eval_rv a gs st e in + let eval_rv_lval lv st = + (* old: *) + (* eval_rv a gs st (Lval lv) *) + + (* new, copied from eval_rv_base to use different ctx for eval_lv (for Mem): *) + (* TODO: deduplicate *) + let do_offs def o = def in (* HACK: no do_offs blessed here *) + match lv with + | (Var v, ofs) -> do_offs (get a gs st (eval_lv oa gs ost (Var v, ofs)) (Some exp)) ofs + (*| Lval (Mem e, ofs) -> do_offs (get a gs st (eval_lv a gs st (Mem e, ofs))) ofs*) + | (Mem e, ofs) -> + (*M.tracel "cast" "Deref: lval: %a\n" d_plainlval lv;*) + let rec contains_vla (t:typ) = match t with + | TPtr (t, _) -> contains_vla t + | TArray(t, None, args) -> true + | TArray(t, Some exp, args) when isConstant exp -> contains_vla t + | TArray(t, Some exp, args) -> true + | _ -> false + in + let b = Mem e, NoOffset in (* base pointer *) + let t = Cilfacade.typeOfLval b in (* static type of base *) + let p = eval_lv oa gs ost b in (* abstract base addresses *) + let v = (* abstract base value *) + let open Addr in + (* pre VLA: *) + (* let cast_ok = function Addr a -> sizeOf t <= sizeOf (get_type_addr a) | _ -> false in *) + let cast_ok = function + | Addr (x, o) -> + begin + let at = get_type_addr (x, o) in + if M.tracing then M.tracel "evalint" "cast_ok %a %a %a\n" Addr.pretty (Addr (x, o)) CilType.Typ.pretty (Cil.unrollType x.vtype) CilType.Typ.pretty at; + if at = TVoid [] then (* HACK: cast from alloc variable is always fine *) + true + else + match Cil.getInteger (sizeOf t), Cil.getInteger (sizeOf at) with + | Some i1, Some i2 -> Cilint.compare_cilint i1 i2 <= 0 + | _ -> + if contains_vla t || contains_vla (get_type_addr (x, o)) then + begin + (* TODO: Is this ok? *) + M.info ~category:Unsound "Casting involving a VLA is assumed to work"; + true + end + else + false + end + | NullPtr | UnknownPtr -> true (* TODO: are these sound? *) + | _ -> false + in + if AD.for_all cast_ok p then + get ~top:(VD.top_value t) a gs st p (Some exp) (* downcasts are safe *) + else + VD.top () (* upcasts not! *) + in + let v' = VD.cast t v in (* cast to the expected type (the abstract type might be something other than t since we don't change addresses upon casts!) *) + if M.tracing then M.tracel "cast" "Ptr-Deref: cast %a to %a = %a!\n" VD.pretty v d_type t VD.pretty v'; + let v' = VD.eval_offset a (fun x -> get a gs st x (Some exp)) v' (convert_offset a gs st ofs) (Some exp) None t in (* handle offset *) + let v' = do_offs v' ofs in (* handle blessed fields? *) + v' + in let set' lval v st = set a gs st (eval_lv oa gs ost lval) (Cilfacade.typeOfLval lval) v ~invariant:false ~ctx in (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) match x with | Var var, o -> @@ -2431,7 +2490,8 @@ struct ) | Mem _, _ -> (* For accesses via pointers, not yet *) - let oldv = eval (Lval x) st in (* TODO: this lval should be evaluated in octx, but its value in ctx *) + let oldv = eval_rv_lval x st in + let oldv = if VD.is_bot oldv then VD.top_value (Cilfacade.typeOfLval x) else oldv in let v = VD.meet oldv c' in if is_some_bot v then raise Deadcode else ( diff --git a/tests/regression/56-witness/19-base-unassume-mem.c b/tests/regression/56-witness/19-base-unassume-mem.c index 7ffad48c56..4b2f01c2bc 100644 --- a/tests/regression/56-witness/19-base-unassume-mem.c +++ b/tests/regression/56-witness/19-base-unassume-mem.c @@ -15,5 +15,7 @@ int main() { __goblint_check(j == 0); // UNKNOWN (intentional by unassume) __goblint_check(i >= 0); __goblint_check(j >= 0); + __goblint_check(i <= 10); + __goblint_check(j <= 10); return 0; } \ No newline at end of file diff --git a/tests/regression/56-witness/19-base-unassume-mem.yml b/tests/regression/56-witness/19-base-unassume-mem.yml index 06a1758d68..37c47d57e4 100644 --- a/tests/regression/56-witness/19-base-unassume-mem.yml +++ b/tests/regression/56-witness/19-base-unassume-mem.yml @@ -25,3 +25,30 @@ string: '*p >= 0' type: assertion format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: ddb9942d-998c-425d-a74f-228044bc38fb + creation_time: 2022-09-01T10:15:07Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-base-0-ged336aa6c-dirty + command_line: '''../../../goblint'' ''--enable'' ''ana.int.interval'' ''--enable'' + ''witness.yaml.enabled'' ''19-base-unassume-mem.c''' + task: + input_files: + - 19-base-unassume-mem.c + input_file_hashes: + 19-base-unassume-mem.c: d71d7065e36b8e0a2c42c96e5f6ea057d28065adae2d85b516989b8d43e77c91 + data_model: LP64 + language: C + location: + file_name: 19-base-unassume-mem.c + file_hash: d71d7065e36b8e0a2c42c96e5f6ea057d28065adae2d85b516989b8d43e77c91 + line: 14 + column: 2 + function: main + loop_invariant: + string: '*p <= 10' + type: assertion + format: C From 60f24850cbe282b32c4db2819f94a90c2845d9d7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 13 Sep 2022 13:50:27 +0300 Subject: [PATCH 037/132] Fix 36-apron/38-branch-global being too trivial --- tests/regression/36-apron/38-branch-global.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/regression/36-apron/38-branch-global.c b/tests/regression/36-apron/38-branch-global.c index cda8e26183..76428224ae 100644 --- a/tests/regression/36-apron/38-branch-global.c +++ b/tests/regression/36-apron/38-branch-global.c @@ -1,10 +1,13 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval -int ARR_SIZE ; +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --disable ana.int.interval +int ARR_SIZE = 10; int main() { int count = 0; while(count= 0); + __goblint_check(count < ARR_SIZE); count++; } + __goblint_check(count == ARR_SIZE); // TODO (requires threshold) return 0 ; } From d6a6786f2d75f536f4cdca316896466feb693a59 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 13 Sep 2022 14:53:12 +0300 Subject: [PATCH 038/132] Add strengthening unassume example from paper --- .../19-apron-unassume-strengthening.c | 13 +++++++++ .../19-apron-unassume-strengthening.yml | 29 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tests/regression/56-witness/19-apron-unassume-strengthening.c create mode 100644 tests/regression/56-witness/19-apron-unassume-strengthening.yml diff --git a/tests/regression/56-witness/19-apron-unassume-strengthening.c b/tests/regression/56-witness/19-apron-unassume-strengthening.c new file mode 100644 index 0000000000..c1de48dda0 --- /dev/null +++ b/tests/regression/56-witness/19-apron-unassume-strengthening.c @@ -0,0 +1,13 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 19-apron-unassume-strengthening.yml --enable ana.apron.strengthening --set sem.int.signed_overflow assume_none +#include + +int main() { + int x, y; + x = 0; + if (x < y) { + __goblint_check(x == 0); // UNKNOWN (intentional by unassume) + __goblint_check(x >= 0); + __goblint_check(x < y); + } + return 0; +} diff --git a/tests/regression/56-witness/19-apron-unassume-strengthening.yml b/tests/regression/56-witness/19-apron-unassume-strengthening.yml new file mode 100644 index 0000000000..44ef995387 --- /dev/null +++ b/tests/regression/56-witness/19-apron-unassume-strengthening.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: dec4db01-03ed-47dd-ae55-c84f8dcd51ed + creation_time: 2022-09-13T11:38:16Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g82e0e31ba-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''19-apron-unassume-strengthening.c'' + ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''--set'' ''dbg.debug'' ''true'' ''--set'' ''printstats'' ''true'' ''--set'' + ''goblint-dir'' ''.goblint-56-19''' + task: + input_files: + - 19-apron-unassume-strengthening.c + input_file_hashes: + 19-apron-unassume-strengthening.c: b2a50e3b423ade600ec2de58fa8ace6ec9a08b85fdc016cbdb7fbe9a3ba80d83 + data_model: LP64 + language: C + location: + file_name: 19-apron-unassume-strengthening.c + file_hash: b2a50e3b423ade600ec2de58fa8ace6ec9a08b85fdc016cbdb7fbe9a3ba80d83 + line: 8 + column: 4 + function: main + loop_invariant: + string: x >= 0 + type: assertion + format: C From bb3e9f1095d3c06860efd5ed04a15727cffce4e1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 13 Sep 2022 14:54:14 +0300 Subject: [PATCH 039/132] Implement strengthening unassume in apron --- src/analyses/apron/apronAnalysis.apron.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index fd1a347ac1..6e2c219283 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -573,14 +573,15 @@ struct | Events.Escape escaped -> Priv.escape ctx.node (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st escaped | Events.Unassume e -> - let apr = AD.bot () in (* empty env *) (* add only relevant vars to env *) let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal |> List.filter AD.varinfo_tracked in if List.for_all (fun v -> not v.vglob) vars then ( - let apr = AD.add_vars apr (List.map V.local vars) in + let apr = ctx.local.apr in + let apr = AD.forget_vars apr (List.map V.local vars) in (* havoc *) let apr = List.fold_left assert_type_bounds apr vars in (* add type bounds to avoid overflow in top state *) - let apr = AD.assert_inv apr e false in - let apr' = AD.join ctx.local.apr apr in + let apr = AD.assert_inv apr e false in (* assume *) + let apr = AD.keep_vars apr (List.map V.local vars) in (* restrict *) + let apr' = AD.join ctx.local.apr apr in (* (strengthening) join *) M.info ~category:Witness "apron unassumed invariant: %a" d_exp e; {ctx.local with apr = apr'} ) From e53470073bcd599aa24648932a217963fa16b19d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 14 Sep 2022 12:33:38 +0300 Subject: [PATCH 040/132] Add single-threaded apron global unassume test --- .../56-witness/20-apron-unassume-global.c | 11 ++++ .../56-witness/20-apron-unassume-global.yml | 56 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 tests/regression/56-witness/20-apron-unassume-global.c create mode 100644 tests/regression/56-witness/20-apron-unassume-global.yml diff --git a/tests/regression/56-witness/20-apron-unassume-global.c b/tests/regression/56-witness/20-apron-unassume-global.c new file mode 100644 index 0000000000..6ad4146ba2 --- /dev/null +++ b/tests/regression/56-witness/20-apron-unassume-global.c @@ -0,0 +1,11 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 20-apron-unassume-global.yml +#include + +int i = 0; +int main() { + while (i < 100) { + i++; + } + assert(i == 100); + return 0; +} diff --git a/tests/regression/56-witness/20-apron-unassume-global.yml b/tests/regression/56-witness/20-apron-unassume-global.yml new file mode 100644 index 0000000000..6f824d4f9f --- /dev/null +++ b/tests/regression/56-witness/20-apron-unassume-global.yml @@ -0,0 +1,56 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 0a72f7b3-7826-4f68-bc7b-25425e95946e + creation_time: 2022-07-26T09:11:03Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g48503c690-dirty + command_line: '''./goblint'' ''--enable'' ''dbg.debug'' ''--enable'' ''dbg.regression'' + ''--html'' ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''20-apron-unassume-global.c''' + task: + input_files: + - 20-apron-unassume-global.c + input_file_hashes: + 20-apron-unassume-global.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + location: + file_name: 20-apron-unassume-global.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 6 + column: 2 + function: main + loop_invariant: + string: 100LL - (long long )i >= 0LL + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 4e078bcd-9e55-4874-a86e-0563927704a5 + creation_time: 2022-07-26T09:11:03Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g48503c690-dirty + command_line: '''./goblint'' ''--enable'' ''dbg.debug'' ''--enable'' ''dbg.regression'' + ''--html'' ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''20-apron-unassume-global.c''' + task: + input_files: + - 20-apron-unassume-global.c + input_file_hashes: + 20-apron-unassume-global.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + location: + file_name: 20-apron-unassume-global.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 6 + column: 2 + function: main + loop_invariant: + string: (long long )i >= 0LL + type: assertion + format: C From d9c9b0d87c64b4dcc53cdc5b4c3dcfd711f1cce7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 14 Sep 2022 12:34:06 +0300 Subject: [PATCH 041/132] Implement apron global unassume --- src/analyses/apron/apronAnalysis.apron.ml | 35 +++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 6e2c219283..b265677a86 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -573,22 +573,27 @@ struct | Events.Escape escaped -> Priv.escape ctx.node (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st escaped | Events.Unassume e -> - (* add only relevant vars to env *) + let ask = Analyses.ask_of_ctx ctx in + let e = replace_deref_exps ctx.ask e in + let (apr, e, v_ins) = read_globals_to_locals ask ctx.global ctx.local e in + let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal |> List.filter AD.varinfo_tracked in - if List.for_all (fun v -> not v.vglob) vars then ( - let apr = ctx.local.apr in - let apr = AD.forget_vars apr (List.map V.local vars) in (* havoc *) - let apr = List.fold_left assert_type_bounds apr vars in (* add type bounds to avoid overflow in top state *) - let apr = AD.assert_inv apr e false in (* assume *) - let apr = AD.keep_vars apr (List.map V.local vars) in (* restrict *) - let apr' = AD.join ctx.local.apr apr in (* (strengthening) join *) - M.info ~category:Witness "apron unassumed invariant: %a" d_exp e; - {ctx.local with apr = apr'} - ) - else ( - M.info ~category:Witness "apron didn't unassume invariant: %a" d_exp e; - ctx.local (* TODO: support unassume with globals *) - ) + let apr = AD.forget_vars apr (List.map V.local vars) in (* havoc *) + let apr = List.fold_left assert_type_bounds apr vars in (* add type bounds to avoid overflow in top state *) + let apr = AD.assert_inv apr e false in (* assume *) + let apr = AD.keep_vars apr (List.map V.local vars) in (* restrict *) + + (* TODO: parallel write_global? *) + let st = VH.fold (fun v v_in st -> + (* TODO: is this sideg fine? *) + write_global ask ctx.global ctx.sideg st v v_in + ) v_ins {ctx.local with apr} + in + let apr = AD.remove_vars st.apr (List.map V.local (VH.values v_ins |> List.of_enum)) in (* remove temporary g#in-s *) + + let st = D.join ctx.local {st with apr} in (* (strengthening) join *) + M.info ~category:Witness "apron unassumed invariant: %a" d_exp e; + st | _ -> st From 6f89ebb5de4e491b12a802a8a5ded9a5e3fc10c8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 14 Sep 2022 13:02:13 +0300 Subject: [PATCH 042/132] Add multi-threaded apron global unassume test --- .../56-witness/21-apron-unassume-priv.c | 25 ++++++++++++++++ .../56-witness/21-apron-unassume-priv.yml | 30 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/regression/56-witness/21-apron-unassume-priv.c create mode 100644 tests/regression/56-witness/21-apron-unassume-priv.yml diff --git a/tests/regression/56-witness/21-apron-unassume-priv.c b/tests/regression/56-witness/21-apron-unassume-priv.c new file mode 100644 index 0000000000..bbbe92d640 --- /dev/null +++ b/tests/regression/56-witness/21-apron-unassume-priv.c @@ -0,0 +1,25 @@ +// SKIP PARAM: --set ana.base.privatization none --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 21-apron-unassume-priv.yml +#include +#include + +int g = 0; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&A); + if (g < 10) + g++; + pthread_mutex_unlock(&A); + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&A); + assert(g >= 0); + assert(g <= 10); + pthread_mutex_unlock(&A); + return 0; +} diff --git a/tests/regression/56-witness/21-apron-unassume-priv.yml b/tests/regression/56-witness/21-apron-unassume-priv.yml new file mode 100644 index 0000000000..257d03e357 --- /dev/null +++ b/tests/regression/56-witness/21-apron-unassume-priv.yml @@ -0,0 +1,30 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 85b0932e-717e-47ea-b7a0-6ed5ca294047 + creation_time: 2022-09-14T09:58:32Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-apron-0-gbb3e9f109-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''21-apron-unassume-priv.c'' + ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''--set'' ''dbg.debug'' ''true'' ''--set'' ''printstats'' ''true'' ''--set'' + ''goblint-dir'' ''.goblint-56-21''' + task: + input_files: + - 21-apron-unassume-priv.c + input_file_hashes: + 21-apron-unassume-priv.c: bc48bad1f602a9b79db764c73e42950810b763f19ebda683b33603c8941f2e80 + data_model: LP64 + language: C + location: + file_name: 21-apron-unassume-priv.c + file_hash: bc48bad1f602a9b79db764c73e42950810b763f19ebda683b33603c8941f2e80 + line: 12 + column: 2 + function: t_fun + loop_invariant: + string: '0 <= g && g <= 10' + type: assertion + format: C + From 3cec958c7ccbeabc3e49ffe784ff6ce2539e7f08 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 14 Sep 2022 13:04:04 +0300 Subject: [PATCH 043/132] Fix apron unassumed invariant info with globals --- src/analyses/apron/apronAnalysis.apron.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index b265677a86..062afee284 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -573,6 +573,7 @@ struct | Events.Escape escaped -> Priv.escape ctx.node (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st escaped | Events.Unassume e -> + let e_orig = e in let ask = Analyses.ask_of_ctx ctx in let e = replace_deref_exps ctx.ask e in let (apr, e, v_ins) = read_globals_to_locals ask ctx.global ctx.local e in @@ -592,7 +593,7 @@ struct let apr = AD.remove_vars st.apr (List.map V.local (VH.values v_ins |> List.of_enum)) in (* remove temporary g#in-s *) let st = D.join ctx.local {st with apr} in (* (strengthening) join *) - M.info ~category:Witness "apron unassumed invariant: %a" d_exp e; + M.info ~category:Witness "apron unassumed invariant: %a" d_exp e_orig; st | _ -> st From d2da3e1656fc532f001d496f253d3f46b19db5ec Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 14 Sep 2022 15:06:34 +0300 Subject: [PATCH 044/132] Allow globals in base unassume, force single-threaded mode for sets --- src/analyses/base.ml | 427 +++++++++++++++++++++---------------------- 1 file changed, 208 insertions(+), 219 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index c3ba6d3f90..78d9a260d1 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2308,230 +2308,219 @@ struct ctx.local let unassume (ctx: (D.t, _, _, _) ctx) e = - let e_cpa = CPA.bot () in - let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal in - if List.for_all (fun v -> not v.vglob) vars then ( - (* TODO: start with empty vars because unassume may unassume values for pointed variables not in the invariant exp *) - (* let e_cpa = List.fold_left (fun e_cpa v -> - CPA.add v (VD.top_value v.vtype) e_cpa - ) e_cpa vars - in *) - (* TODO: structural unassume instead of invariant hack *) - let e_d = - let ctx_with_local local = - (* The usual recursion trick for ctx. *) - (* Must change ctx used by ask to also use new st (not ctx.local), otherwise recursive EvalInt queries use outdated state. *) - (* Note: query is just called on base, but not any other analyses. Potentially imprecise, but seems to be sufficient for now. *) - let rec ctx' asked = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> query' asked q) - ; local - } - and query': type a. Queries.Set.t -> a Queries.t -> a Queries.result = fun asked q -> - let anyq = Queries.Any q in - if Queries.Set.mem anyq asked then - Queries.Result.top q (* query cycle *) - else ( - let asked' = Queries.Set.add anyq asked in - match q with - | MayEscape _ - | MayBePublic _ - | MayBePublicWithout _ - | MustBeProtectedBy _ - | MustLockset - | MustBeAtomic - | MustBeSingleThreaded - | MustBeUniqueThread - | CurrentThreadId - | MayBeThreadReturn - | PartAccess _ - | IsHeapVar _ - | IsMultiple _ - | CreatedThreads - | MustJoinedThreads -> - (* These queries are safe to ask from outside, - where base doesn't have the partial top local state. - They are also needed for sensible eval behavior via [inv_exp] - such that everything wouldn't be may escaped. *) - ctx.ask q - | _ -> - (* Other queries are not safe, because they would - query the local value state instead of top. - Therefore, these are answered only by base on the - partial top local state. *) - query (ctx' asked') q - ) - in - ctx' Queries.Set.empty + (* TODO: structural unassume instead of invariant hack *) + let e_d = + let ctx_with_local ~single local = + (* The usual recursion trick for ctx. *) + (* Must change ctx used by ask to also use new st (not ctx.local), otherwise recursive EvalInt queries use outdated state. *) + (* Note: query is just called on base, but not any other analyses. Potentially imprecise, but seems to be sufficient for now. *) + let rec ctx' asked = + { ctx with + ask = (fun (type a) (q: a Queries.t) -> query' asked q) + ; local + } + and query': type a. Queries.Set.t -> a Queries.t -> a Queries.result = fun asked q -> + let anyq = Queries.Any q in + if Queries.Set.mem anyq asked then + Queries.Result.top q (* query cycle *) + else ( + let asked' = Queries.Set.add anyq asked in + match q with + | MustBeSingleThreaded when single -> true + | MayEscape _ + | MayBePublic _ + | MayBePublicWithout _ + | MustBeProtectedBy _ + | MustLockset + | MustBeAtomic + | MustBeSingleThreaded + | MustBeUniqueThread + | CurrentThreadId + | MayBeThreadReturn + | PartAccess _ + | IsHeapVar _ + | IsMultiple _ + | CreatedThreads + | MustJoinedThreads -> + (* These queries are safe to ask from outside, + where base doesn't have the partial top local state. + They are also needed for sensible eval behavior via [inv_exp] + such that everything wouldn't be may escaped. *) + ctx.ask q + | _ -> + (* Other queries are not safe, because they would + query the local value state instead of top. + Therefore, these are answered only by base on the + partial top local state. *) + query (ctx' asked') q + ) in - let f st = - let local: D.t = {ctx.local with cpa = e_cpa} in - let octx = ctx_with_local st in (* original ctx with non-top values *) - (* TODO: deduplicate with invariant *) - let ctx = ctx_with_local local in - let module UnassumeEval = - struct - module D = D - module V = V - module G = G - - let oa = Analyses.ask_of_ctx octx - let ost = octx.local - - let apply_invariant oldv newv = - match oldv, newv with - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o && AD.mem (Addr.unknown_ptr ()) n -> *) - (* `Address (AD.join o n) *) - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o -> `Address n *) - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) n -> `Address o *) - | _ -> VD.meet oldv newv - - (* all updates happen in ctx with top values *) - - let refine_lv_fallback ctx a gs st lval value tv = - if M.tracing then M.tracec "invariant" "Restricting %a with %a\n" d_lval lval VD.pretty value; - let addr = eval_lv oa gs ost lval in - if (AD.is_top addr) then st - else - let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) - let t_lval = Cilfacade.typeOfLval lval in - let oldval = if VD.is_bot oldval then VD.top_value t_lval else oldval in - let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in - let state_with_excluded = set a gs st addr t_lval value ~invariant:false ~ctx in (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) - let value = get a gs state_with_excluded addr None in - let new_val = apply_invariant oldval value in - if M.tracing then M.traceu "invariant" "New value is %a\n" VD.pretty new_val; - (* make that address meet the invariant, i.e exclusion sets will be joined *) - if is_some_bot new_val then ( - if M.tracing then M.tracel "branch" "C The branch %B is dead!\n" tv; - raise Analyses.Deadcode - ) - else if VD.is_bot new_val - (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) - then set a gs st addr t_lval value ~invariant:false ~ctx (* no *_raw because this is not a real assignment *) - else set a gs st addr t_lval new_val ~invariant:false ~ctx (* no *_raw because this is not a real assignment *) - - let refine_lv ctx a gs st c x c' pretty exp = - let eval_rv_lval lv st = - (* old: *) - (* eval_rv a gs st (Lval lv) *) - - (* new, copied from eval_rv_base to use different ctx for eval_lv (for Mem): *) - (* TODO: deduplicate *) - let do_offs def o = def in (* HACK: no do_offs blessed here *) - match lv with - | (Var v, ofs) -> do_offs (get a gs st (eval_lv oa gs ost (Var v, ofs)) (Some exp)) ofs - (*| Lval (Mem e, ofs) -> do_offs (get a gs st (eval_lv a gs st (Mem e, ofs))) ofs*) - | (Mem e, ofs) -> - (*M.tracel "cast" "Deref: lval: %a\n" d_plainlval lv;*) - let rec contains_vla (t:typ) = match t with - | TPtr (t, _) -> contains_vla t - | TArray(t, None, args) -> true - | TArray(t, Some exp, args) when isConstant exp -> contains_vla t - | TArray(t, Some exp, args) -> true + ctx' Queries.Set.empty + in + let f st = + (* TODO: start with empty vars because unassume may unassume values for pointed variables not in the invariant exp *) + let local: D.t = {ctx.local with cpa = CPA.bot ()} in + let octx = ctx_with_local ~single:false st in (* original ctx with non-top values *) + (* TODO: deduplicate with invariant *) + let ctx = ctx_with_local ~single:true local in + let module UnassumeEval = + struct + module D = D + module V = V + module G = G + + let oa = Analyses.ask_of_ctx octx + let ost = octx.local + + let apply_invariant oldv newv = + match oldv, newv with + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o && AD.mem (Addr.unknown_ptr ()) n -> *) + (* `Address (AD.join o n) *) + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o -> `Address n *) + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) n -> `Address o *) + | _ -> VD.meet oldv newv + + (* all updates happen in ctx with top values *) + + let refine_lv_fallback ctx a gs st lval value tv = + if M.tracing then M.tracec "invariant" "Restricting %a with %a\n" d_lval lval VD.pretty value; + let addr = eval_lv oa gs ost lval in + if (AD.is_top addr) then st + else + let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) + let t_lval = Cilfacade.typeOfLval lval in + let oldval = if VD.is_bot oldval then VD.top_value t_lval else oldval in + let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in + let state_with_excluded = set a gs st addr t_lval value ~invariant:false ~ctx in (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) + let value = get a gs state_with_excluded addr None in + let new_val = apply_invariant oldval value in + if M.tracing then M.traceu "invariant" "New value is %a\n" VD.pretty new_val; + (* make that address meet the invariant, i.e exclusion sets will be joined *) + if is_some_bot new_val then ( + if M.tracing then M.tracel "branch" "C The branch %B is dead!\n" tv; + raise Analyses.Deadcode + ) + else if VD.is_bot new_val + (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) + then set a gs st addr t_lval value ~invariant:false ~ctx (* no *_raw because this is not a real assignment *) + else set a gs st addr t_lval new_val ~invariant:false ~ctx (* no *_raw because this is not a real assignment *) + + let refine_lv ctx a gs st c x c' pretty exp = + let eval_rv_lval lv st = + (* old: *) + (* eval_rv a gs st (Lval lv) *) + + (* new, copied from eval_rv_base to use different ctx for eval_lv (for Mem): *) + (* TODO: deduplicate *) + let do_offs def o = def in (* HACK: no do_offs blessed here *) + match lv with + | (Var v, ofs) -> do_offs (get a gs st (eval_lv oa gs ost (Var v, ofs)) (Some exp)) ofs + (*| Lval (Mem e, ofs) -> do_offs (get a gs st (eval_lv a gs st (Mem e, ofs))) ofs*) + | (Mem e, ofs) -> + (*M.tracel "cast" "Deref: lval: %a\n" d_plainlval lv;*) + let rec contains_vla (t:typ) = match t with + | TPtr (t, _) -> contains_vla t + | TArray(t, None, args) -> true + | TArray(t, Some exp, args) when isConstant exp -> contains_vla t + | TArray(t, Some exp, args) -> true + | _ -> false + in + let b = Mem e, NoOffset in (* base pointer *) + let t = Cilfacade.typeOfLval b in (* static type of base *) + let p = eval_lv oa gs ost b in (* abstract base addresses *) + let v = (* abstract base value *) + let open Addr in + (* pre VLA: *) + (* let cast_ok = function Addr a -> sizeOf t <= sizeOf (get_type_addr a) | _ -> false in *) + let cast_ok = function + | Addr (x, o) -> + begin + let at = get_type_addr (x, o) in + if M.tracing then M.tracel "evalint" "cast_ok %a %a %a\n" Addr.pretty (Addr (x, o)) CilType.Typ.pretty (Cil.unrollType x.vtype) CilType.Typ.pretty at; + if at = TVoid [] then (* HACK: cast from alloc variable is always fine *) + true + else + match Cil.getInteger (sizeOf t), Cil.getInteger (sizeOf at) with + | Some i1, Some i2 -> Cilint.compare_cilint i1 i2 <= 0 + | _ -> + if contains_vla t || contains_vla (get_type_addr (x, o)) then + begin + (* TODO: Is this ok? *) + M.info ~category:Unsound "Casting involving a VLA is assumed to work"; + true + end + else + false + end + | NullPtr | UnknownPtr -> true (* TODO: are these sound? *) | _ -> false in - let b = Mem e, NoOffset in (* base pointer *) - let t = Cilfacade.typeOfLval b in (* static type of base *) - let p = eval_lv oa gs ost b in (* abstract base addresses *) - let v = (* abstract base value *) - let open Addr in - (* pre VLA: *) - (* let cast_ok = function Addr a -> sizeOf t <= sizeOf (get_type_addr a) | _ -> false in *) - let cast_ok = function - | Addr (x, o) -> - begin - let at = get_type_addr (x, o) in - if M.tracing then M.tracel "evalint" "cast_ok %a %a %a\n" Addr.pretty (Addr (x, o)) CilType.Typ.pretty (Cil.unrollType x.vtype) CilType.Typ.pretty at; - if at = TVoid [] then (* HACK: cast from alloc variable is always fine *) - true - else - match Cil.getInteger (sizeOf t), Cil.getInteger (sizeOf at) with - | Some i1, Some i2 -> Cilint.compare_cilint i1 i2 <= 0 - | _ -> - if contains_vla t || contains_vla (get_type_addr (x, o)) then - begin - (* TODO: Is this ok? *) - M.info ~category:Unsound "Casting involving a VLA is assumed to work"; - true - end - else - false - end - | NullPtr | UnknownPtr -> true (* TODO: are these sound? *) - | _ -> false - in - if AD.for_all cast_ok p then - get ~top:(VD.top_value t) a gs st p (Some exp) (* downcasts are safe *) - else - VD.top () (* upcasts not! *) - in - let v' = VD.cast t v in (* cast to the expected type (the abstract type might be something other than t since we don't change addresses upon casts!) *) - if M.tracing then M.tracel "cast" "Ptr-Deref: cast %a to %a = %a!\n" VD.pretty v d_type t VD.pretty v'; - let v' = VD.eval_offset a (fun x -> get a gs st x (Some exp)) v' (convert_offset a gs st ofs) (Some exp) None t in (* handle offset *) - let v' = do_offs v' ofs in (* handle blessed fields? *) - v' - in - let set' lval v st = set a gs st (eval_lv oa gs ost lval) (Cilfacade.typeOfLval lval) v ~invariant:false ~ctx in (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) - match x with - | Var var, o -> - (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) - let oldv = get_var a gs st var in - let oldv = if VD.is_bot oldv then VD.top_value var.vtype else oldv in - let offs = convert_offset oa gs ost o in - let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in - let v = VD.meet oldv newv in - if is_some_bot v then raise Deadcode - else ( - if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v pretty c VD.pretty c'; - let r = set' (Var var,NoOffset) v st in - if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; - r - ) - | Mem _, _ -> - (* For accesses via pointers, not yet *) - let oldv = eval_rv_lval x st in - let oldv = if VD.is_bot oldv then VD.top_value (Cilfacade.typeOfLval x) else oldv in - let v = VD.meet oldv c' in - if is_some_bot v then raise Deadcode - else ( - if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; - set' x v st - ) - - (* all evals happen in octx with non-top values *) - (* must be down here to make evals in refines call the real ones *) - - let eval_rv a gs st e = eval_rv oa gs ost e - let eval_rv_address a gs st e = eval_rv_address oa gs ost e - let eval_lv a gs st lv = eval_lv oa gs ost lv - - (* don't meet with current octx values when propagating inverse operands down *) - let id_meet_down ~old ~c = c - let fd_meet_down ~old ~c = c - end - in - let module Unassume = BaseInvariant.Make (UnassumeEval) in - try - Unassume.invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true - with Deadcode -> (* contradiction in unassume *) - D.bot () + if AD.for_all cast_ok p then + get ~top:(VD.top_value t) a gs st p (Some exp) (* downcasts are safe *) + else + VD.top () (* upcasts not! *) + in + let v' = VD.cast t v in (* cast to the expected type (the abstract type might be something other than t since we don't change addresses upon casts!) *) + if M.tracing then M.tracel "cast" "Ptr-Deref: cast %a to %a = %a!\n" VD.pretty v d_type t VD.pretty v'; + let v' = VD.eval_offset a (fun x -> get a gs st x (Some exp)) v' (convert_offset a gs st ofs) (Some exp) None t in (* handle offset *) + let v' = do_offs v' ofs in (* handle blessed fields? *) + v' + in + let set' lval v st = set a gs st (eval_lv oa gs ost lval) (Cilfacade.typeOfLval lval) v ~invariant:false ~ctx in (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) + match x with + | Var var, o -> + (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) + let oldv = get_var a gs st var in + let oldv = if VD.is_bot oldv then VD.top_value var.vtype else oldv in + let offs = convert_offset oa gs ost o in + let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in + let v = VD.meet oldv newv in + if is_some_bot v then raise Deadcode + else ( + if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v pretty c VD.pretty c'; + let r = set' (Var var,NoOffset) v st in + if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; + r + ) + | Mem _, _ -> + (* For accesses via pointers, not yet *) + let oldv = eval_rv_lval x st in + let oldv = if VD.is_bot oldv then VD.top_value (Cilfacade.typeOfLval x) else oldv in + let v = VD.meet oldv c' in + if is_some_bot v then raise Deadcode + else ( + if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; + set' x v st + ) + + (* all evals happen in octx with non-top values *) + (* must be down here to make evals in refines call the real ones *) + + let eval_rv a gs st e = eval_rv oa gs ost e + let eval_rv_address a gs st e = eval_rv_address oa gs ost e + let eval_lv a gs st lv = eval_lv oa gs ost lv + + (* don't meet with current octx values when propagating inverse operands down *) + let id_meet_down ~old ~c = c + let fd_meet_down ~old ~c = c + end in - let f local = D.join ctx.local (f local) in (* join ctx.local to remain sound *) - let module DFP = LocalFixpoint.Make (D) in - if M.tracing then M.traceli "unassume" "base unassuming\n"; - let r = DFP.lfp ~init:ctx.local f in (* start from ctx.local instead of D.bot () to avoid invariant on bot *) - if M.tracing then M.traceu "unassume" "base unassumed\n"; - r + let module Unassume = BaseInvariant.Make (UnassumeEval) in + try + Unassume.invariant ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local e true + with Deadcode -> (* contradiction in unassume *) + D.bot () in - M.info ~category:Witness "base unassumed invariant: %a" d_exp e; - M.debug ~category:Witness "base unassumed state: %a" D.pretty e_d; - e_d (* ctx.local is joined in above *) - ) - else ( - M.info ~category:Witness "base didn't unassume invariant: %a" d_exp e; - ctx.local (* TODO: support unassume with globals *) - ) + let f local = D.join ctx.local (f local) in (* join ctx.local to remain sound *) + let module DFP = LocalFixpoint.Make (D) in + if M.tracing then M.traceli "unassume" "base unassuming\n"; + let r = DFP.lfp ~init:ctx.local f in (* start from ctx.local instead of D.bot () to avoid invariant on bot *) + if M.tracing then M.traceu "unassume" "base unassumed\n"; + r + in + M.info ~category:Witness "base unassumed invariant: %a" d_exp e; + M.debug ~category:Witness "base unassumed state: %a" D.pretty e_d; + e_d (* ctx.local is joined in above *) let event ctx e octx = let st: store = ctx.local in From b910b27e2a53529e01818903cd2906c128e7b080 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 13 Sep 2022 17:55:12 +0300 Subject: [PATCH 045/132] Remove side effects from base priv invariant write_global-s Invariant only refines, so value is already contained in the unprotected invariant. Avoids pointless side effect. --- src/analyses/basePriv.ml | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 7535a18d11..19633cd3df 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -202,7 +202,8 @@ struct r *) let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = let cpa' = CPA.add x v st.cpa in - sideg (V.global x) (CPA.singleton x v); + if not invariant then + sideg (V.global x) (CPA.singleton x v); {st with cpa = cpa'} (* let write_global ask getg sideg cpa x v = let cpa' = write_global ask getg sideg cpa x v in @@ -282,8 +283,10 @@ struct else CPA.add x v st.cpa in - if M.tracing then M.tracel "priv" "WRITE GLOBAL SIDE %a = %a\n" d_varinfo x VD.pretty v; - sideg (V.global x) (CPA.singleton x v); + if not invariant then ( + if M.tracing then M.tracel "priv" "WRITE GLOBAL SIDE %a = %a\n" d_varinfo x VD.pretty v; + sideg (V.global x) (CPA.singleton x v) + ); {st with cpa = cpa'} (* let write_global ask getg sideg cpa x v = let cpa' = write_global ask getg sideg cpa x v in @@ -396,9 +399,11 @@ struct VD.join (CPA.find x st.cpa) (getg (V.protected x)) let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = - sideg (V.unprotected x) v; - if !GU.earlyglobs then (* earlyglobs workaround for 13/60 *) - sideg (V.protected x) v; + if not invariant then ( + sideg (V.unprotected x) v; + if !GU.earlyglobs then (* earlyglobs workaround for 13/60 *) + sideg (V.protected x) v + ); if is_unprotected ask x then st else @@ -570,7 +575,7 @@ struct let s = current_lockset ask in let t = current_thread ask in let cpa' = CPA.add x v st.cpa in - if not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then + if not invariant && not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then sideg (V.global x) (G.create_weak (GWeak.singleton s (ThreadMap.singleton t v))); {st with cpa = cpa'} @@ -627,7 +632,7 @@ struct let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = let s = current_lockset ask in let cpa' = CPA.add x v st.cpa in - if not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then + if not invariant && not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then sideg (V.global x) (G.create_weak (GWeak.singleton s v)); {st with cpa = cpa'} @@ -697,7 +702,7 @@ struct let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = let s = current_lockset ask in let cpa' = CPA.add x v st.cpa in - if not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then + if not invariant && not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then sideg (V.global x) (G.create_weak (GWeak.singleton s v)); {st with cpa = cpa'; priv = W.add x st.priv} @@ -838,7 +843,7 @@ struct ) l vv in let cpa' = CPA.add x v st.cpa in - if not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then ( + if not invariant && not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then ( let v = distr_init getg x v in sideg (V.global x) (G.create_weak (GWeak.singleton s v)) ); @@ -981,7 +986,7 @@ struct let p' = P.add x (MinLocksets.singleton s) p in let p' = P.map (fun s' -> MinLocksets.add s s') p' in let cpa' = CPA.add x v st.cpa in - if not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then ( + if not invariant && not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then ( let v = distr_init getg x v in sideg (V.global x) (G.create_weak (GWeak.singleton s (GWeakW.singleton s v))) ); @@ -1143,7 +1148,7 @@ struct ) l vv in let cpa' = CPA.add x v st.cpa in - if not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then ( + if not invariant && not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then ( let v = distr_init getg x v in sideg (V.global x) (G.create_weak (GWeak.singleton s (GWeakW.singleton s v))) ); From 3815cc1ad0c97f4750beed5bd93e50efe43cdc1a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 14 Sep 2022 10:47:49 +0300 Subject: [PATCH 046/132] Optimize and document base priv invariant write W --- src/analyses/basePriv.ml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 19633cd3df..eb80618c8b 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -204,6 +204,7 @@ struct let cpa' = CPA.add x v st.cpa in if not invariant then sideg (V.global x) (CPA.singleton x v); + (* Unlock after invariant will still side effect refined value from CPA, because cannot distinguish from non-invariant write. *) {st with cpa = cpa'} (* let write_global ask getg sideg cpa x v = let cpa' = write_global ask getg sideg cpa x v in @@ -286,6 +287,7 @@ struct if not invariant then ( if M.tracing then M.tracel "priv" "WRITE GLOBAL SIDE %a = %a\n" d_varinfo x VD.pretty v; sideg (V.global x) (CPA.singleton x v) + (* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write. *) ); {st with cpa = cpa'} (* let write_global ask getg sideg cpa x v = @@ -403,6 +405,7 @@ struct sideg (V.unprotected x) v; if !GU.earlyglobs then (* earlyglobs workaround for 13/60 *) sideg (V.protected x) v + (* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write since W is implicit. *) ); if is_unprotected ask x then st @@ -577,6 +580,7 @@ struct let cpa' = CPA.add x v st.cpa in if not invariant && not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then sideg (V.global x) (G.create_weak (GWeak.singleton s (ThreadMap.singleton t v))); + (* Unlock after invariant will not side effect refined value from weak, because it's not side effected there. *) {st with cpa = cpa'} let lock ask getg (st: BaseComponents (D).t) m = @@ -634,6 +638,7 @@ struct let cpa' = CPA.add x v st.cpa in if not invariant && not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then sideg (V.global x) (G.create_weak (GWeak.singleton s v)); + (* Unlock after invariant will not side effect refined value from weak, because it's not side effected there. *) {st with cpa = cpa'} let lock ask getg (st: BaseComponents (D).t) m = @@ -704,7 +709,12 @@ struct let cpa' = CPA.add x v st.cpa in if not invariant && not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then sideg (V.global x) (G.create_weak (GWeak.singleton s v)); - {st with cpa = cpa'; priv = W.add x st.priv} + let w' = if not invariant then + W.add x st.priv + else + st.priv (* No need to add invariant to W because it doesn't matter for reads after invariant, only unlocks. *) + in + {st with cpa = cpa'; priv = w'} let lock ask getg (st: BaseComponents (D).t) m = let s = current_lockset ask in @@ -846,6 +856,7 @@ struct if not invariant && not (!GU.earlyglobs && is_excluded_from_earlyglobs x) then ( let v = distr_init getg x v in sideg (V.global x) (G.create_weak (GWeak.singleton s v)) + (* Unlock after invariant will still side effect refined value from CPA, because cannot distinguish from non-invariant write. *) ); {st with cpa = cpa'; priv = (v', l)} @@ -982,7 +993,11 @@ struct let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = let s = current_lockset ask in let (w, p) = st.priv in - let w' = W.add x (MinLocksets.singleton s) w in + let w' = if not invariant then + W.add x (MinLocksets.singleton s) w + else + w (* No need to add invariant to W because it doesn't matter for reads after invariant, only unlocks. *) + in let p' = P.add x (MinLocksets.singleton s) p in let p' = P.map (fun s' -> MinLocksets.add s s') p' in let cpa' = CPA.add x v st.cpa in @@ -1140,7 +1155,11 @@ struct let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v = let s = current_lockset ask in let ((w, p), (vv, l)) = st.priv in - let w' = W.add x (MinLocksets.singleton s) w in + let w' = if not invariant then + W.add x (MinLocksets.singleton s) w + else + w (* No need to add invariant to W because it doesn't matter for reads after invariant, only unlocks. *) + in let p' = P.add x (MinLocksets.singleton s) p in let p' = P.map (fun s' -> MinLocksets.add s s') p' in let v' = L.fold (fun m _ acc -> From f01a9d297127b0575c535f789af654b287971dbb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 14 Sep 2022 15:27:07 +0300 Subject: [PATCH 047/132] Add multi-threaded base global unassume test --- .../56-witness/22-base-unassume-priv.c | 25 ++++++++++++++++ .../56-witness/22-base-unassume-priv.yml | 30 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/regression/56-witness/22-base-unassume-priv.c create mode 100644 tests/regression/56-witness/22-base-unassume-priv.yml diff --git a/tests/regression/56-witness/22-base-unassume-priv.c b/tests/regression/56-witness/22-base-unassume-priv.c new file mode 100644 index 0000000000..14991928f8 --- /dev/null +++ b/tests/regression/56-witness/22-base-unassume-priv.c @@ -0,0 +1,25 @@ +// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 22-base-unassume-priv.yml +#include +#include + +int g = 0; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&A); + if (g < 10) + g++; + pthread_mutex_unlock(&A); + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&A); + assert(g >= 0); + assert(g <= 10); + pthread_mutex_unlock(&A); + return 0; +} diff --git a/tests/regression/56-witness/22-base-unassume-priv.yml b/tests/regression/56-witness/22-base-unassume-priv.yml new file mode 100644 index 0000000000..3ebeca0b4e --- /dev/null +++ b/tests/regression/56-witness/22-base-unassume-priv.yml @@ -0,0 +1,30 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 85b0932e-717e-47ea-b7a0-6ed5ca294047 + creation_time: 2022-09-14T09:58:32Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-apron-0-gbb3e9f109-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''22-base-unassume-priv.c'' + ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''--set'' ''dbg.debug'' ''true'' ''--set'' ''printstats'' ''true'' ''--set'' + ''goblint-dir'' ''.goblint-56-21''' + task: + input_files: + - 22-base-unassume-priv.c + input_file_hashes: + 22-base-unassume-priv.c: bc48bad1f602a9b79db764c73e42950810b763f19ebda683b33603c8941f2e80 + data_model: LP64 + language: C + location: + file_name: 22-base-unassume-priv.c + file_hash: bc48bad1f602a9b79db764c73e42950810b763f19ebda683b33603c8941f2e80 + line: 12 + column: 2 + function: t_fun + loop_invariant: + string: '0 <= g && g <= 10' + type: assertion + format: C + From 8a6b50b83c9e35d86dd0cbda170958b80a09ab9b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 14 Sep 2022 15:27:35 +0300 Subject: [PATCH 048/132] Fix protection-based base priv V tracing --- src/analyses/basePriv.ml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index eb80618c8b..ad97d5b56a 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -376,12 +376,20 @@ struct include VarinfoV (* [g]' *) let name () = "unprotected" let show x = show x ^ ":unprotected" (* distinguishable variant names for html *) + include Printable.SimpleShow (struct + type nonrec t = t + let show = show + end) end module VProt = struct include VarinfoV (* [g] *) let name () = "protected" let show x = show x ^ ":protected" (* distinguishable variant names for html *) + include Printable.SimpleShow (struct + type nonrec t = t + let show = show + end) end module V = struct From f5c44ffe5f244bd6e3d9c8a0ad344918773fa06a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 14 Sep 2022 15:29:13 +0300 Subject: [PATCH 049/132] Implement base global unassume --- src/analyses/base.ml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 78d9a260d1..9a2627f600 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2360,7 +2360,7 @@ struct let f st = (* TODO: start with empty vars because unassume may unassume values for pointed variables not in the invariant exp *) let local: D.t = {ctx.local with cpa = CPA.bot ()} in - let octx = ctx_with_local ~single:false st in (* original ctx with non-top values *) + let octx = ctx_with_local ~single:false (D.join ctx.local st) in (* original ctx with non-top values *) (* TODO: deduplicate with invariant *) let ctx = ctx_with_local ~single:true local in let module UnassumeEval = @@ -2511,16 +2511,23 @@ struct with Deadcode -> (* contradiction in unassume *) D.bot () in - let f local = D.join ctx.local (f local) in (* join ctx.local to remain sound *) let module DFP = LocalFixpoint.Make (D) in if M.tracing then M.traceli "unassume" "base unassuming\n"; - let r = DFP.lfp ~init:ctx.local f in (* start from ctx.local instead of D.bot () to avoid invariant on bot *) + let r = DFP.lfp f in if M.tracing then M.traceu "unassume" "base unassumed\n"; r in M.info ~category:Witness "base unassumed invariant: %a" d_exp e; M.debug ~category:Witness "base unassumed state: %a" D.pretty e_d; - e_d (* ctx.local is joined in above *) + (* Perform actual [set]-s with final unassumed values. + This invokes [Priv.write_global], which was suppressed above. *) + let e_d' = + CPA.fold (fun x v acc -> + let addr: AD.t = AD.from_var_offset (x, `NoOffset) in + set (Analyses.ask_of_ctx ctx) ~ctx ~invariant:false ctx.global acc addr x.vtype v + ) e_d.cpa ctx.local + in + D.join ctx.local e_d' let event ctx e octx = let st: store = ctx.local in From 4517c1ec41d36ad9057d2aac65d1d595d18ddfed Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 14 Sep 2022 18:00:48 +0300 Subject: [PATCH 050/132] Add hacky widening tokens for unassume --- src/analyses/unassumeAnalysis.ml | 3 +- src/framework/control.ml | 1 + src/util/wideningTokens.ml | 94 +++++++++++++++++++ .../56-witness/12-apron-unassume-branch.c | 2 +- 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/util/wideningTokens.ml diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index fb2d955e58..83f86be9be 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -189,7 +189,8 @@ struct begin match es with | x :: xs -> let e = List.fold_left (fun a b -> Cil.(BinOp (LAnd, a, b, intType))) x xs in - ctx.emit (Unassume e) + ctx.emit (Unassume e); + WideningTokens.perform (CilType.Exp.show e) | [] -> () end; diff --git a/src/framework/control.ml b/src/framework/control.ml index e40854bab4..1fcd52103c 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -29,6 +29,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( |> lift (get_int "dbg.limit.widen" > 0) (module LimitLifter) |> lift (get_bool "ana.opt.equal" && not (get_bool "ana.opt.hashcons")) (module OptEqual) |> lift (get_bool "ana.opt.hashcons") (module HashconsLifter) + |> lift true (module WideningTokens.Lifter) ) in GobConfig.building_spec := false; Analyses.control_spec_c := (module S1.C); diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml new file mode 100644 index 0000000000..5ffafdf3d2 --- /dev/null +++ b/src/util/wideningTokens.ml @@ -0,0 +1,94 @@ +module Token = Basetype.RawStrings + +type handler = Token.t -> unit +let handler: handler ref = ref (fun _ -> failwith "Unhandled token") + +let perform t = !handler t + +let handle ~(with_:handler) f = + let old_handler = !handler in + handler := with_; + Fun.protect ~finally:(fun () -> + handler := old_handler + ) f + + +open Prelude +open Analyses + +module Lifter (S: Spec): Spec = +struct + module TS = SetDomain.ToppedSet (Token) (struct let topname = "Top" end) + module D = + struct + include Lattice.Prod (S.D) (TS) + let unlift (d, _) = d + let lift d = (d, TS.bot ()) + + let printXml f (d, t) = + (* BatPrintf.fprintf f "\n%a" Spec.D.printXml x *) + BatPrintf.fprintf f "\n%a%a" S.D.printXml d TS.printXml t + + let widen (d1, t1) (d2, t2) = + let d' = if TS.is_empty (TS.diff t2 t1) then + S.D.widen d1 d2 + else + S.D.join d1 d2 + in + (d', TS.join t1 t2) + end + module G = S.G + module C = S.C + module V = S.V + + let name () = S.name ()^" with widening tokens" + + type marshal = S.marshal + let init = S.init + let finalize = S.finalize + + let should_join (x, _) (y, _) = S.should_join x y + + let startstate v = (S.startstate v, TS.bot ()) + let exitstate v = (S.exitstate v, TS.bot ()) + let morphstate v (d, t) = (S.morphstate v d, t) + + let context fd = S.context fd % D.unlift + + (* TODO: propagate tokens *) + let conv ctx = + { ctx with local = D.unlift ctx.local + ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) + } + + let lift_fun ctx f g h = + let ts = ref (snd ctx.local) in + let d = handle ~with_:(fun t -> ts := TS.add t !ts) (fun () -> + h (g (conv ctx)) + ) + in + f d !ts + + let lift' d ts = (d, ts) + + let sync ctx reason = lift_fun ctx lift' S.sync ((|>) reason) + + let enter ctx r f args = + let liftmap l ts = List.map (fun (x,y) -> (x, ts), (y, ts)) l in + lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) + + let query ctx (type a) (q: a Queries.t): a Queries.result = + lift_fun ctx Fun.const S.query (fun (x) -> x q) + let assign ctx lv e = lift_fun ctx lift' S.assign ((|>) e % (|>) lv) + let vdecl ctx v = lift_fun ctx lift' S.vdecl ((|>) v) + let branch ctx e tv = lift_fun ctx lift' S.branch ((|>) tv % (|>) e) + let body ctx f = lift_fun ctx lift' S.body ((|>) f) + let return ctx r f = lift_fun ctx lift' S.return ((|>) f % (|>) r) + let asm ctx = lift_fun ctx lift' S.asm identity + let skip ctx = lift_fun ctx lift' S.skip identity + let special ctx r f args = lift_fun ctx lift' S.special ((|>) args % (|>) f % (|>) r) + let combine ctx r fe f args fc es = lift_fun ctx lift' S.combine (fun p -> p r fe f args fc (D.unlift es)) (* TODO: use tokens from es *) + + let threadenter ctx lval f args = lift_fun ctx (fun l ts -> List.map (Fun.flip lift' ts) l) S.threadenter ((|>) args % (|>) f % (|>) lval) + let threadspawn ctx lval f args fctx = lift_fun ctx lift' S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) +end \ No newline at end of file diff --git a/tests/regression/56-witness/12-apron-unassume-branch.c b/tests/regression/56-witness/12-apron-unassume-branch.c index 99edbcecb2..c534c561f3 100644 --- a/tests/regression/56-witness/12-apron-unassume-branch.c +++ b/tests/regression/56-witness/12-apron-unassume-branch.c @@ -6,6 +6,6 @@ int main() { while (i < 100) { i++; } - assert(i == 100); // TODO: avoid widening when unassume inside loop + assert(i == 100); return 0; } From a5ee11f86612ea05641f958d2b66c08ad43a4a02 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 15 Sep 2022 11:35:24 +0300 Subject: [PATCH 051/132] Add test for widening tokens on globals --- .../56-witness/22-base-unassume-priv.c | 2 +- .../56-witness/23-base-unassume-priv2.c | 26 ++++++++++++++++ .../56-witness/23-base-unassume-priv2.yml | 30 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/regression/56-witness/23-base-unassume-priv2.c create mode 100644 tests/regression/56-witness/23-base-unassume-priv2.yml diff --git a/tests/regression/56-witness/22-base-unassume-priv.c b/tests/regression/56-witness/22-base-unassume-priv.c index 14991928f8..76023100f9 100644 --- a/tests/regression/56-witness/22-base-unassume-priv.c +++ b/tests/regression/56-witness/22-base-unassume-priv.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 22-base-unassume-priv.yml +// PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 22-base-unassume-priv.yml #include #include diff --git a/tests/regression/56-witness/23-base-unassume-priv2.c b/tests/regression/56-witness/23-base-unassume-priv2.c new file mode 100644 index 0000000000..54ddf9aa21 --- /dev/null +++ b/tests/regression/56-witness/23-base-unassume-priv2.c @@ -0,0 +1,26 @@ +// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 23-base-unassume-priv2.yml --set solvers.td3.side_widen always +#include +#include + +int g = 0; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&A); + assert(1); // TODO: fix Lock and Unassume event order so this extra node isn't necessary + if (g < 10) + g++; + pthread_mutex_unlock(&A); + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&A); + assert(g >= 0); + assert(g <= 10); // TODO: widening tokens on globals + pthread_mutex_unlock(&A); + return 0; +} diff --git a/tests/regression/56-witness/23-base-unassume-priv2.yml b/tests/regression/56-witness/23-base-unassume-priv2.yml new file mode 100644 index 0000000000..7500fb87dd --- /dev/null +++ b/tests/regression/56-witness/23-base-unassume-priv2.yml @@ -0,0 +1,30 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 85b0932e-717e-47ea-b7a0-6ed5ca294047 + creation_time: 2022-09-14T09:58:32Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-apron-0-gbb3e9f109-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''23-base-unassume-priv2.c'' + ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''--set'' ''dbg.debug'' ''true'' ''--set'' ''printstats'' ''true'' ''--set'' + ''goblint-dir'' ''.goblint-56-21''' + task: + input_files: + - 23-base-unassume-priv2.c + input_file_hashes: + 23-base-unassume-priv2.c: bc48bad1f602a9b79db764c73e42950810b763f19ebda683b33603c8941f2e80 + data_model: LP64 + language: C + location: + file_name: 23-base-unassume-priv2.c + file_hash: bc48bad1f602a9b79db764c73e42950810b763f19ebda683b33603c8941f2e80 + line: 11 + column: 2 + function: t_fun + loop_invariant: + string: '0 <= g && g <= 10' + type: assertion + format: C + From 415adf06cf4362c1b0e4ae6ff4889c7c30f5247e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 15 Sep 2022 11:10:02 +0300 Subject: [PATCH 052/132] Add hacky widening tokens for base unassume globals --- src/analyses/base.ml | 10 +++-- src/analyses/mCP.ml | 13 ++++-- src/util/wideningTokens.ml | 44 +++++++++++++++++-- .../56-witness/23-base-unassume-priv2.c | 4 +- 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 9a2627f600..40925b8049 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2522,10 +2522,12 @@ struct (* Perform actual [set]-s with final unassumed values. This invokes [Priv.write_global], which was suppressed above. *) let e_d' = - CPA.fold (fun x v acc -> - let addr: AD.t = AD.from_var_offset (x, `NoOffset) in - set (Analyses.ask_of_ctx ctx) ~ctx ~invariant:false ctx.global acc addr x.vtype v - ) e_d.cpa ctx.local + WideningTokens.with_side_token (CilType.Exp.show e) (fun () -> + CPA.fold (fun x v acc -> + let addr: AD.t = AD.from_var_offset (x, `NoOffset) in + set (Analyses.ask_of_ctx ctx) ~ctx ~invariant:false ctx.global acc addr x.vtype v + ) e_d.cpa ctx.local + ) in D.join ctx.local e_d' diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index b8efd4a624..c6aa5fa744 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -160,9 +160,14 @@ struct if not (get_bool "exp.single-threaded") then iter (uncurry spawn_one) @@ group_assoc_eq Basetype.Variables.equal xs - let do_sideg ctx (xs:(V.t * G.t) list) = - let side_one v d = - ctx.sideg v @@ fold_left G.join (G.bot ()) d + let do_sideg ctx (xs:(V.t * (WideningTokens.TS.t * G.t)) list) = + let side_one v dts = + let side_one_ts ts d = + WideningTokens.with_side_tokens ts (fun () -> + ctx.sideg v @@ fold_left G.join (G.bot ()) d + ) + in + iter (uncurry side_one_ts) @@ group_assoc_eq WideningTokens.TS.equal dts in iter (uncurry side_one) @@ group_assoc_eq V.equal xs @@ -298,7 +303,7 @@ struct | None -> (fun v d -> failwith ("Cannot \"spawn\" in " ^ tfname ^ " context.")) in let sideg = match sides with - | Some sides -> (fun v g -> sides := (v, g) :: !sides) + | Some sides -> (fun v g -> sides := (v, (!WideningTokens.side_tokens, g)) :: !sides) | None -> (fun v g -> failwith ("Cannot \"sideg\" in " ^ tfname ^ " context.")) in let emit = match emits with diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index 5ffafdf3d2..227feb1372 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -1,4 +1,5 @@ module Token = Basetype.RawStrings +module TS = SetDomain.ToppedSet (Token) (struct let topname = "Top" end) type handler = Token.t -> unit let handler: handler ref = ref (fun _ -> failwith "Unhandled token") @@ -13,12 +14,28 @@ let handle ~(with_:handler) f = ) f +let side_tokens: TS.t ref = ref (TS.bot ()) + +let with_side_token t f = + let old_side_tokens = !side_tokens in + side_tokens := TS.add t old_side_tokens; + Fun.protect ~finally:(fun () -> + side_tokens := old_side_tokens + ) f + +let with_side_tokens ts f = + let old_side_tokens = !side_tokens in + side_tokens := ts; + Fun.protect ~finally:(fun () -> + side_tokens := old_side_tokens + ) f + + open Prelude open Analyses module Lifter (S: Spec): Spec = struct - module TS = SetDomain.ToppedSet (Token) (struct let topname = "Top" end) module D = struct include Lattice.Prod (S.D) (TS) @@ -37,7 +54,24 @@ struct in (d', TS.join t1 t2) end - module G = S.G + module G = + struct + include Lattice.Prod (S.G) (TS) + let unlift (d, _) = d + let lift d = (d, TS.bot ()) + + let printXml f (d, t) = + (* BatPrintf.fprintf f "\n%a" Spec.D.printXml x *) + BatPrintf.fprintf f "\n%a%a" S.G.printXml d TS.printXml t + + let widen (d1, t1) (d2, t2) = + let d' = if TS.is_empty (TS.diff t2 t1) then + S.G.widen d1 d2 + else + S.G.join d1 d2 + in + (d', TS.join t1 t2) + end module C = S.C module V = S.V @@ -56,9 +90,11 @@ struct let context fd = S.context fd % D.unlift (* TODO: propagate tokens *) - let conv ctx = + let conv (ctx: (D.t, G.t, C.t, V.t) ctx): (S.D.t, S.G.t, S.C.t, S.V.t) ctx = { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) + ; split = (fun d es -> ctx.split (d, snd ctx.local) es) + ; global = (fun g -> G.unlift (ctx.global g)) + ; sideg = (fun v g -> ctx.sideg v (g, TS.join (snd ctx.local) !side_tokens)) } let lift_fun ctx f g h = diff --git a/tests/regression/56-witness/23-base-unassume-priv2.c b/tests/regression/56-witness/23-base-unassume-priv2.c index 54ddf9aa21..a49d7e083b 100644 --- a/tests/regression/56-witness/23-base-unassume-priv2.c +++ b/tests/regression/56-witness/23-base-unassume-priv2.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 23-base-unassume-priv2.yml --set solvers.td3.side_widen always +// PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 23-base-unassume-priv2.yml --set solvers.td3.side_widen always #include #include @@ -20,7 +20,7 @@ int main() { pthread_mutex_lock(&A); assert(g >= 0); - assert(g <= 10); // TODO: widening tokens on globals + assert(g <= 10); pthread_mutex_unlock(&A); return 0; } From 6a85ab1adee42538caaba1fd741ceccc581e03ac Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 15 Sep 2022 11:20:19 +0300 Subject: [PATCH 053/132] Make local widening tokens reuse explicitly controlled --- src/analyses/base.ml | 11 +++++++++-- src/util/wideningTokens.ml | 17 +++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 40925b8049..4b9da6bc96 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -375,7 +375,12 @@ struct ThreadFlag.is_multi (Analyses.ask_of_ctx ctx) in if M.tracing then M.tracel "sync" "sync multi=%B earlyglobs=%B\n" multi !GU.earlyglobs; - if !GU.earlyglobs || multi then Priv.sync (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) ctx.local reason else ctx.local + if !GU.earlyglobs || multi then + WideningTokens.with_local_side_tokens (fun () -> + Priv.sync (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) ctx.local reason + ) + else + ctx.local let sync ctx reason = sync' (reason :> [`Normal | `Join | `Return | `Init | `Thread]) ctx @@ -2540,7 +2545,9 @@ struct | Events.Unlock addr when ThreadFlag.is_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *) if addr = UnknownPtr then M.info ~category:Unsound "Unknown mutex unlocked, base privatization unsound"; (* TODO: something more sound *) - Priv.unlock (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr + WideningTokens.with_local_side_tokens (fun () -> + Priv.unlock (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr + ) | Events.Escape escaped -> Priv.escape (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) st escaped | Events.EnterMultiThreaded -> diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index 227feb1372..4ee46b915a 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -30,6 +30,17 @@ let with_side_tokens ts f = side_tokens := old_side_tokens ) f +let local_tokens: TS.t ref = ref (TS.bot ()) + +let with_local_tokens ts f = + let old_local_tokens = !local_tokens in + local_tokens := ts; + Fun.protect ~finally:(fun () -> + local_tokens := old_local_tokens + ) f + +let with_local_side_tokens f = + with_side_tokens !local_tokens f open Prelude open Analyses @@ -94,13 +105,15 @@ struct { ctx with local = D.unlift ctx.local ; split = (fun d es -> ctx.split (d, snd ctx.local) es) ; global = (fun g -> G.unlift (ctx.global g)) - ; sideg = (fun v g -> ctx.sideg v (g, TS.join (snd ctx.local) !side_tokens)) + ; sideg = (fun v g -> ctx.sideg v (g, !side_tokens)) } let lift_fun ctx f g h = let ts = ref (snd ctx.local) in let d = handle ~with_:(fun t -> ts := TS.add t !ts) (fun () -> - h (g (conv ctx)) + with_local_tokens (snd ctx.local) (fun () -> + h (g (conv ctx)) + ) ) in f d !ts From 53adb3e21fad15bb974c170ef630c11ea22d4a8a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 15 Sep 2022 13:47:58 +0300 Subject: [PATCH 054/132] Fix octx' in MCP do_emits using wrong local --- src/analyses/mCP.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index c6aa5fa744..dff4997956 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -205,7 +205,7 @@ struct let octx'' = outer_ctx "do_emits" ~spawns ~sides ~emits octx in let f post_all (n,(module S:MCPSpec),(d,od)) = let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "do_emits" ~splits ~post_all ctx'' n d in - let octx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "do_emits" ~splits ~post_all octx'' n d in + let octx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "do_emits" ~splits ~post_all octx'' n od in n, repr @@ S.event ctx' e octx' in let d, q = map_deadcode f @@ spec_list2 ctx.local octx.local in From df6218ff68036aa5049850e71899f046fd71491a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 15 Sep 2022 13:48:37 +0300 Subject: [PATCH 055/132] Fix normal emits not run on splits --- src/analyses/mCP.ml | 36 +++++++++++-------- .../56-witness/23-base-unassume-priv2.c | 2 +- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index dff4997956..91c31b3f4a 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -171,10 +171,13 @@ struct in iter (uncurry side_one) @@ group_assoc_eq V.equal xs - let rec do_splits ctx pv (xs:(int * (Obj.t * Events.t list)) list) = - let split_one n (d,emits) = + let rec do_splits ctx pv (xs:(int * (Obj.t * Events.t list)) list) emits = + let split_one n (d,emits') = let nv = assoc_replace (n,d) pv in - ctx.split (do_emits ctx emits nv) [] + (* Do split-specific emits before general emits. + [emits] and [do_emits] are in reverse order. + [emits'] is in normal order. *) + ctx.split (do_emits ctx (emits @ List.rev emits') nv) [] in iter (uncurry split_one) xs @@ -212,11 +215,14 @@ struct if M.tracing then M.tracel "event" "%a\n before: %a\n after:%a\n" Events.pretty e D.pretty ctx.local D.pretty d; do_sideg ctx !sides; do_spawns ctx !spawns; - do_splits ctx d !splits; + do_splits ctx d !splits !emits; let d = do_emits ctx !emits d in if q then raise Deadcode else ctx_with_local ctx d in - let ctx' = List.fold_left do_emit (ctx_with_local ctx xs) emits in + if M.tracing then M.traceli "event" "do_emits:\n"; + (* [emits] is in reverse order. *) + let ctx' = List.fold_left do_emit (ctx_with_local ctx xs) (List.rev emits) in + if M.tracing then M.traceu "event" "\n"; ctx'.local and branch (ctx:(D.t, G.t, C.t, V.t) ctx) (e:exp) (tv:bool) = @@ -232,7 +238,7 @@ struct let d, q = map_deadcode f @@ spec_list ctx.local in do_sideg ctx !sides; do_spawns ctx !spawns; - do_splits ctx d !splits; + do_splits ctx d !splits !emits; let d = do_emits ctx !emits d in if q then raise Deadcode else d @@ -307,7 +313,7 @@ struct | None -> (fun v g -> failwith ("Cannot \"sideg\" in " ^ tfname ^ " context.")) in let emit = match emits with - | Some emits -> (fun e -> emits := e :: !emits) + | Some emits -> (fun e -> emits := e :: !emits) (* [emits] is in reverse order. *) | None -> (fun _ -> failwith ("Cannot \"emit\" in " ^ tfname ^ " context.")) in let querycache = Queries.Hashtbl.create 13 in @@ -346,7 +352,7 @@ struct let d, q = map_deadcode f @@ spec_list ctx.local in do_sideg ctx !sides; do_spawns ctx !spawns; - do_splits ctx d !splits; + do_splits ctx d !splits !emits; let d = do_emits ctx !emits d in if q then raise Deadcode else d @@ -364,7 +370,7 @@ struct let d, q = map_deadcode f @@ spec_list ctx.local in do_sideg ctx !sides; do_spawns ctx !spawns; - do_splits ctx d !splits; + do_splits ctx d !splits !emits; let d = do_emits ctx !emits d in if q then raise Deadcode else d @@ -381,7 +387,7 @@ struct let d, q = map_deadcode f @@ spec_list ctx.local in do_sideg ctx !sides; do_spawns ctx !spawns; - do_splits ctx d !splits; + do_splits ctx d !splits !emits; let d = do_emits ctx !emits d in if q then raise Deadcode else d @@ -398,7 +404,7 @@ struct let d, q = map_deadcode f @@ spec_list ctx.local in do_sideg ctx !sides; do_spawns ctx !spawns; - do_splits ctx d !splits; + do_splits ctx d !splits !emits; let d = do_emits ctx !emits d in if q then raise Deadcode else d @@ -416,7 +422,7 @@ struct let d, q = map_deadcode f @@ spec_list ctx.local in do_sideg ctx !sides; do_spawns ctx !spawns; - do_splits ctx d !splits; + do_splits ctx d !splits !emits; let d = do_emits ctx !emits d in if q then raise Deadcode else d @@ -433,7 +439,7 @@ struct let d, q = map_deadcode f @@ spec_list ctx.local in do_sideg ctx !sides; do_spawns ctx !spawns; - do_splits ctx d !splits; + do_splits ctx d !splits !emits; let d = do_emits ctx !emits d in if q then raise Deadcode else d @@ -450,7 +456,7 @@ struct let d, q = map_deadcode f @@ spec_list ctx.local in do_sideg ctx !sides; do_spawns ctx !spawns; - do_splits ctx d !splits; + do_splits ctx d !splits !emits; let d = do_emits ctx !emits d in if q then raise Deadcode else d @@ -467,7 +473,7 @@ struct let d, q = map_deadcode f @@ spec_list ctx.local in do_sideg ctx !sides; do_spawns ctx !spawns; - do_splits ctx d !splits; + do_splits ctx d !splits !emits; let d = do_emits ctx !emits d in if q then raise Deadcode else d diff --git a/tests/regression/56-witness/23-base-unassume-priv2.c b/tests/regression/56-witness/23-base-unassume-priv2.c index a49d7e083b..f495ae7978 100644 --- a/tests/regression/56-witness/23-base-unassume-priv2.c +++ b/tests/regression/56-witness/23-base-unassume-priv2.c @@ -7,7 +7,7 @@ pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; void *t_fun(void *arg) { pthread_mutex_lock(&A); - assert(1); // TODO: fix Lock and Unassume event order so this extra node isn't necessary + if (g < 10) g++; pthread_mutex_unlock(&A); From 42c06d002b4f76f65615461b65a848fee2bea1a4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 15 Sep 2022 14:04:23 +0300 Subject: [PATCH 056/132] Use YAML witness entry UUIDs as widening tokens --- src/analyses/apron/apronAnalysis.apron.ml | 2 +- src/analyses/base.ml | 8 ++++---- src/analyses/unassumeAnalysis.ml | 21 ++++++++++++++------- src/domains/events.ml | 4 ++-- src/util/wideningTokens.ml | 7 +++++++ 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 062afee284..1ebb7ff7fb 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -572,7 +572,7 @@ struct Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st | Events.Escape escaped -> Priv.escape ctx.node (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st escaped - | Events.Unassume e -> + | Events.Unassume {exp = e; _} -> let e_orig = e in let ask = Analyses.ask_of_ctx ctx in let e = replace_deref_exps ctx.ask e in diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4b9da6bc96..61cae3f59c 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2312,7 +2312,7 @@ struct (* D.join ctx.local @@ *) ctx.local - let unassume (ctx: (D.t, _, _, _) ctx) e = + let unassume (ctx: (D.t, _, _, _) ctx) e uuids = (* TODO: structural unassume instead of invariant hack *) let e_d = let ctx_with_local ~single local = @@ -2527,7 +2527,7 @@ struct (* Perform actual [set]-s with final unassumed values. This invokes [Priv.write_global], which was suppressed above. *) let e_d' = - WideningTokens.with_side_token (CilType.Exp.show e) (fun () -> + WideningTokens.with_side_tokens' (WideningTokens.TS.of_list uuids) (fun () -> CPA.fold (fun x v acc -> let addr: AD.t = AD.from_var_offset (x, `NoOffset) in set (Analyses.ask_of_ctx ctx) ~ctx ~invariant:false ctx.global acc addr x.vtype v @@ -2555,8 +2555,8 @@ struct | Events.AssignSpawnedThread (lval, tid) -> (* TODO: is this type right? *) set ~ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local (eval_lv (Analyses.ask_of_ctx ctx) ctx.global ctx.local lval) (Cilfacade.typeOfLval lval) (`Thread (ValueDomain.Threads.singleton tid)) - | Events.Unassume e -> - unassume ctx e + | Events.Unassume {exp; uuids} -> + unassume ctx exp uuids | _ -> ctx.local end diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 83f86be9be..709a17fc6d 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -26,10 +26,15 @@ struct let locator: Locator.t ref = ref (Locator.create ()) (* empty default, so don't have to use option everywhere *) - let invs: Cil.exp NH.t = NH.create 100 + type inv = { + exp: Cil.exp; + uuid: string; + } + + let invs: inv NH.t = NH.create 100 let fun_pres: Cil.exp FH.t = FH.create 100 - let pre_invs: Cil.exp EH.t NH.t = NH.create 100 + let pre_invs: inv EH.t NH.t = NH.create 100 let init _ = locator := Locator.create (); (* TODO: add Locator.clear *) @@ -77,6 +82,7 @@ struct NH.clear pre_invs; let unassume_entry (entry: YamlWitnessType.Entry.t) = + let uuid = entry.metadata.uuid in let unassume_nodes_invariant ~loc ~nodes inv = let msgLoc: M.Location.t = CilLocation loc in @@ -89,7 +95,7 @@ struct match InvariantParser.parse_cil inv_parser ~fundec ~loc inv_cabs with | Ok inv_exp -> M.debug ~category:Witness ~loc:msgLoc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; - NH.add invs n inv_exp + NH.add invs n {exp = inv_exp; uuid} | Error e -> M.error ~category:Witness ~loc:msgLoc "CIL couldn't parse invariant: %s" inv; M.info ~category:Witness ~loc:msgLoc "invariant has undefined variables or side effects: %s" inv @@ -130,7 +136,7 @@ struct M.debug ~category:Witness ~loc:msgLoc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; if not (NH.mem pre_invs n) then NH.replace pre_invs n (EH.create 10); - EH.add (NH.find pre_invs n) pre_exp inv_exp + EH.add (NH.find pre_invs n) pre_exp {exp = inv_exp; uuid} | Error e -> M.error ~category:Witness ~loc:msgLoc "CIL couldn't parse invariant: %s" inv; M.info ~category:Witness ~loc:msgLoc "invariant has undefined variables or side effects: %s" inv @@ -188,9 +194,10 @@ struct in begin match es with | x :: xs -> - let e = List.fold_left (fun a b -> Cil.(BinOp (LAnd, a, b, intType))) x xs in - ctx.emit (Unassume e); - WideningTokens.perform (CilType.Exp.show e) + let e = List.fold_left (fun a {exp = b; _} -> Cil.(BinOp (LAnd, a, b, intType))) x.exp xs in + let uuids = x.uuid :: List.map (fun {uuid; _} -> uuid) xs in + ctx.emit (Unassume {exp = e; uuids}); + List.iter WideningTokens.perform uuids | [] -> () end; diff --git a/src/domains/events.ml b/src/domains/events.ml index 8833359fb6..b41f1d413d 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -9,7 +9,7 @@ type t = | AssignSpawnedThread of lval * ThreadIdDomain.Thread.t (** Assign spawned thread's ID to lval. *) | Access of {var_opt: CilType.Varinfo.t option; kind: AccessKind.t} (** Access varinfo (unknown if None). *) | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) - | Unassume of exp + | Unassume of {exp: CilType.Exp.t; uuids: string list} let pretty () = function | Lock m -> dprintf "Lock %a" LockDomain.Lockset.Lock.pretty m @@ -20,4 +20,4 @@ let pretty () = function | AssignSpawnedThread (lval, tid) -> dprintf "AssignSpawnedThread (%a, %a)" d_lval lval ThreadIdDomain.Thread.pretty tid | Access {var_opt; kind} -> dprintf "Access {var_opt=%a, kind=%a}" (docOpt (CilType.Varinfo.pretty ())) var_opt AccessKind.pretty kind | Assign {lval; exp} -> dprintf "Assign {lval=%a, exp=%a}" CilType.Lval.pretty lval CilType.Exp.pretty exp - | Unassume e -> dprintf "Unassume %a" d_exp e + | Unassume {exp; uuids} -> dprintf "Unassume {exp=%a; uuids=%a}" d_exp exp (docList Pretty.text) uuids diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index 4ee46b915a..a2e28f539c 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -30,6 +30,13 @@ let with_side_tokens ts f = side_tokens := old_side_tokens ) f +let with_side_tokens' ts f = + let old_side_tokens = !side_tokens in + side_tokens := TS.join ts old_side_tokens; + Fun.protect ~finally:(fun () -> + side_tokens := old_side_tokens + ) f + let local_tokens: TS.t ref = ref (TS.bot ()) let with_local_tokens ts f = From cd6c9d26948aea5657aa721a7f6eb60136969252 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 15 Sep 2022 14:13:08 +0300 Subject: [PATCH 057/132] Add widening tokens to apron unassume --- src/analyses/apron/apronAnalysis.apron.ml | 21 ++++++++----- .../56-witness/24-apron-unassume-priv2.c | 26 ++++++++++++++++ .../56-witness/24-apron-unassume-priv2.yml | 30 +++++++++++++++++++ 3 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 tests/regression/56-witness/24-apron-unassume-priv2.c create mode 100644 tests/regression/56-witness/24-apron-unassume-priv2.yml diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 1ebb7ff7fb..5bc3513662 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -566,13 +566,15 @@ struct | Events.Unlock addr when ThreadFlag.is_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *) if addr = UnknownPtr then M.info ~category:Unsound "Unknown mutex unlocked, apron privatization unsound"; (* TODO: something more sound *) - Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr + WideningTokens.with_local_side_tokens (fun () -> + Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr + ) (* No need to handle escape because escaped variables are always referenced but this analysis only considers unreferenced variables. *) | Events.EnterMultiThreaded -> Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st | Events.Escape escaped -> Priv.escape ctx.node (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st escaped - | Events.Unassume {exp = e; _} -> + | Events.Unassume {exp = e; uuids} -> let e_orig = e in let ask = Analyses.ask_of_ctx ctx in let e = replace_deref_exps ctx.ask e in @@ -585,10 +587,13 @@ struct let apr = AD.keep_vars apr (List.map V.local vars) in (* restrict *) (* TODO: parallel write_global? *) - let st = VH.fold (fun v v_in st -> - (* TODO: is this sideg fine? *) - write_global ask ctx.global ctx.sideg st v v_in - ) v_ins {ctx.local with apr} + let st = + WideningTokens.with_side_tokens' (WideningTokens.TS.of_list uuids) (fun () -> + VH.fold (fun v v_in st -> + (* TODO: is this sideg fine? *) + write_global ask ctx.global ctx.sideg st v v_in + ) v_ins {ctx.local with apr} + ) in let apr = AD.remove_vars st.apr (List.map V.local (VH.values v_ins |> List.of_enum)) in (* remove temporary g#in-s *) @@ -616,7 +621,9 @@ struct let new_value = AD.join old_value st in RH.replace results ctx.node new_value; end; - Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `Return | `Init | `Thread]) + 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]) + ) let init marshal = Priv.init () diff --git a/tests/regression/56-witness/24-apron-unassume-priv2.c b/tests/regression/56-witness/24-apron-unassume-priv2.c new file mode 100644 index 0000000000..2dc3d067a1 --- /dev/null +++ b/tests/regression/56-witness/24-apron-unassume-priv2.c @@ -0,0 +1,26 @@ +// SKIP PARAM: --set ana.base.privatization none --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 24-apron-unassume-priv2.yml --set solvers.td3.side_widen always +#include +#include + +int g = 0; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&A); + + if (g < 10) + g++; + pthread_mutex_unlock(&A); + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&A); + assert(g >= 0); + assert(g <= 10); + pthread_mutex_unlock(&A); + return 0; +} diff --git a/tests/regression/56-witness/24-apron-unassume-priv2.yml b/tests/regression/56-witness/24-apron-unassume-priv2.yml new file mode 100644 index 0000000000..01abee3ee8 --- /dev/null +++ b/tests/regression/56-witness/24-apron-unassume-priv2.yml @@ -0,0 +1,30 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 85b0932e-717e-47ea-b7a0-6ed5ca294047 + creation_time: 2022-09-14T09:58:32Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-apron-0-gbb3e9f109-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''24-apron-unassume-priv2.c'' + ''--set'' ''ana.activated[+]'' ''apron'' ''--enable'' ''witness.yaml.enabled'' + ''--set'' ''dbg.debug'' ''true'' ''--set'' ''printstats'' ''true'' ''--set'' + ''goblint-dir'' ''.goblint-56-21''' + task: + input_files: + - 24-apron-unassume-priv2.c + input_file_hashes: + 24-apron-unassume-priv2.c: bc48bad1f602a9b79db764c73e42950810b763f19ebda683b33603c8941f2e80 + data_model: LP64 + language: C + location: + file_name: 24-apron-unassume-priv2.c + file_hash: bc48bad1f602a9b79db764c73e42950810b763f19ebda683b33603c8941f2e80 + line: 11 + column: 2 + function: t_fun + loop_invariant: + string: '0 <= g && g <= 10' + type: assertion + format: C + From d8485190d7aaf142d2ba6f97ddc0305ce9b8c8f5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 15 Sep 2022 14:18:42 +0300 Subject: [PATCH 058/132] Extract widening token domain functor, add leq --- src/util/wideningTokens.ml | 46 ++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index a2e28f539c..1565e5989a 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -52,43 +52,41 @@ let with_local_side_tokens f = open Prelude open Analyses +module Dom (D: Lattice.S) = +struct + include Lattice.Prod (D) (TS) + let unlift (d, _) = d + let lift d = (d, TS.bot ()) + + let leq (d1, t1) (d2, t2) = + D.leq d1 d2 (* ignore tokens for order *) + + (* join also joins tokens *) + + let widen (d1, t1) (d2, t2) = + let d' = if TS.is_empty (TS.diff t2 t1) then + D.widen d1 d2 + else + D.join d1 d2 + in + (d', TS.join t1 t2) +end + module Lifter (S: Spec): Spec = struct module D = struct - include Lattice.Prod (S.D) (TS) - let unlift (d, _) = d - let lift d = (d, TS.bot ()) + include Dom (S.D) let printXml f (d, t) = - (* BatPrintf.fprintf f "\n%a" Spec.D.printXml x *) BatPrintf.fprintf f "\n%a%a" S.D.printXml d TS.printXml t - - let widen (d1, t1) (d2, t2) = - let d' = if TS.is_empty (TS.diff t2 t1) then - S.D.widen d1 d2 - else - S.D.join d1 d2 - in - (d', TS.join t1 t2) end module G = struct - include Lattice.Prod (S.G) (TS) - let unlift (d, _) = d - let lift d = (d, TS.bot ()) + include Dom (S.G) let printXml f (d, t) = - (* BatPrintf.fprintf f "\n%a" Spec.D.printXml x *) BatPrintf.fprintf f "\n%a%a" S.G.printXml d TS.printXml t - - let widen (d1, t1) (d2, t2) = - let d' = if TS.is_empty (TS.diff t2 t1) then - S.G.widen d1 d2 - else - S.G.join d1 d2 - in - (d', TS.join t1 t2) end module C = S.C module V = S.V From 5b16195f2d7c172dd20234e254c1a13794085bc1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Oct 2022 13:33:42 +0300 Subject: [PATCH 059/132] Fix BaseInvariant indentation --- src/analyses/apron/apronAnalysis.apron.ml | 6 +- src/analyses/base.ml | 12 +- src/analyses/baseInvariant.ml | 256 +++++++++++----------- 3 files changed, 137 insertions(+), 137 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index b2f70cb95d..f8f1b670a7 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -635,9 +635,9 @@ struct let st = WideningTokens.with_side_tokens' (WideningTokens.TS.of_list uuids) (fun () -> VH.fold (fun v v_in st -> - (* TODO: is this sideg fine? *) - write_global ask ctx.global ctx.sideg st v v_in - ) v_ins {ctx.local with apr} + (* TODO: is this sideg fine? *) + write_global ask ctx.global ctx.sideg st v v_in + ) v_ins {ctx.local with apr} ) in let apr = AD.remove_vars st.apr (List.map V.local (VH.values v_ins |> List.of_enum)) in (* remove temporary g#in-s *) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index ceeeaccd2c..f69622c362 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2318,15 +2318,15 @@ struct | CreatedThreads | MustJoinedThreads -> (* These queries are safe to ask from outside, - where base doesn't have the partial top local state. - They are also needed for sensible eval behavior via [inv_exp] - such that everything wouldn't be may escaped. *) + where base doesn't have the partial top local state. + They are also needed for sensible eval behavior via [inv_exp] + such that everything wouldn't be may escaped. *) ctx.ask q | _ -> (* Other queries are not safe, because they would - query the local value state instead of top. - Therefore, these are answered only by base on the - partial top local state. *) + query the local value state instead of top. + Therefore, these are answered only by base on the + partial top local state. *) query (ctx' asked') q ) in diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 8c45044360..203d0ab865 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -46,17 +46,17 @@ struct let invariant_fallback ctx a (gs:V.t -> G.t) st exp tv = (* We use a recursive helper function so that x != 0 is false can be handled - * as x == 0 is true etc *) + * as x == 0 is true etc *) let rec helper (op: binop) (lval: lval) (value: VD.t) (tv: bool) = match (op, lval, value, tv) with (* The true-branch where x == value: *) | Eq, x, value, true -> if M.tracing then M.tracec "invariant" "Yes, %a equals %a\n" d_lval x VD.pretty value; (match value with - | `Int n -> - let ikind = Cilfacade.get_ikind_exp (Lval lval) in - Some (x, `Int (ID.cast_to ikind n)) - | _ -> Some(x, value)) + | `Int n -> + let ikind = Cilfacade.get_ikind_exp (Lval lval) in + Some (x, `Int (ID.cast_to ikind n)) + | _ -> Some(x, value)) (* The false-branch for x == value: *) | Eq, x, value, false -> begin match value with @@ -83,7 +83,7 @@ struct (* | `Address a -> Some (x, value) *) | _ -> (* We can't say anything else, exclusion sets are finite, so not - * being in one means an infinite number of values *) + * being in one means an infinite number of values *) if M.tracing then M.tracec "invariant" "Failed! (not a definite value)\n"; None end @@ -141,21 +141,21 @@ struct -> derived_invariant (BinOp (op, c1, c2, t)) tv | BinOp(op, CastE (TInt (ik, _) as t1, Lval x), rval, typ) -> (match eval_rv a gs st (Lval x) with - | `Int v -> - (* This is tricky: It it is not sufficient to check that ID.cast_to_ik v = v - * If there is one domain that knows this to be true and the other does not, we - * should still impose the invariant. E.g. i -> ([1,5]; Not {0}[byte]) *) - if VD.is_safe_cast t1 (Cilfacade.typeOfLval x) then - derived_invariant (BinOp (op, Lval x, rval, typ)) tv - else - None - | _ -> None) + | `Int v -> + (* This is tricky: It it is not sufficient to check that ID.cast_to_ik v = v + * If there is one domain that knows this to be true and the other does not, we + * should still impose the invariant. E.g. i -> ([1,5]; Not {0}[byte]) *) + if VD.is_safe_cast t1 (Cilfacade.typeOfLval x) then + derived_invariant (BinOp (op, Lval x, rval, typ)) tv + else + None + | _ -> None) | BinOp(op, rval, CastE (TInt (_, _) as ti, Lval x), typ) -> derived_invariant (BinOp (switchedOp op, CastE(ti, Lval x), rval, typ)) tv (* Cases like if (x) are treated like if (x != 0) *) | Lval x -> (* There are two correct ways of doing it: "if ((int)x != 0)" or "if (x != (typeof(x))0))" - * Because we try to avoid casts (and use a more precise address domain) we use the latter *) + * Because we try to avoid casts (and use a more precise address domain) we use the latter *) helper Ne x (null_val (Cilfacade.typeOf exp)) tv | UnOp (LNot,uexp,typ) -> derived_invariant uexp (not tv) | _ -> @@ -181,7 +181,7 @@ struct let warn_and_top_on_zero x = if GobOption.exists (BI.equal BI.zero) (ID.to_int x) then (M.error ~category:M.Category.Integer.div_by_zero ~tags:[CWE 369] "Must Undefined Behavior: Second argument of div or mod is 0, continuing with top"; - ID.top_of ikind) + ID.top_of ikind) else x in @@ -211,8 +211,8 @@ struct (* If b must be zero, we have must UB *) let b = warn_and_top_on_zero b in (* Integer division means we need to add the remainder, so instead of just `a = c*b` we have `a = c*b + a%b`. - * However, a%b will give [-b+1, b-1] for a=top, but we only want the positive/negative side depending on the sign of c*b. - * If c*b = 0 or it can be positive or negative, we need the full range for the remainder. *) + * However, a%b will give [-b+1, b-1] for a=top, but we only want the positive/negative side depending on the sign of c*b. + * If c*b = 0 or it can be positive or negative, we need the full range for the remainder. *) let rem = let is_pos = ID.to_bool @@ ID.gt (ID.mul b c) (ID.of_int ikind BI.zero) = Some true in let is_neg = ID.to_bool @@ ID.lt (ID.mul b c) (ID.of_int ikind BI.zero) = Some true in @@ -226,12 +226,12 @@ struct (* If b must be zero, we have must UB *) let b = warn_and_top_on_zero b in (* a' = a/b*b + c and derived from it b' = (a-c)/(a/b) - * The idea is to formulate a' as quotient * divisor + remainder. *) + * The idea is to formulate a' as quotient * divisor + remainder. *) let a' = ID.add (ID.mul (ID.div a b) b) c in let b' = ID.div (ID.sub a c) (ID.div a b) in (* However, for [2,4]%2 == 1 this only gives [3,4]. - * If the upper bound of a is divisible by b, we can also meet with the result of a/b*b - c to get the precise [3,3]. - * If b is negative we have to look at the lower bound. *) + * If the upper bound of a is divisible by b, we can also meet with the result of a/b*b - c to get the precise [3,3]. + * If b is negative we have to look at the lower bound. *) let is_divisible bound = match bound a with | Some ba -> ID.rem (ID.of_int ikind ba) b |> ID.to_int = Some BI.zero @@ -259,35 +259,35 @@ struct let both x = x, x in let m = ID.meet a b in (match op, ID.to_bool c with - | Eq, Some true - | Ne, Some false -> both m (* def. equal: if they compare equal, both values must be from the meet *) - | Eq, Some false - | Ne, Some true -> (* def. unequal *) - (* Both values can not be in the meet together, but it's not sound to exclude the meet from both. - * e.g. a=[0,1], b=[1,2], meet a b = [1,1], but (a != b) does not imply a=[0,0], b=[2,2] since others are possible: a=[1,1], b=[2,2] - * Only if a is a definite value, we can exclude it from b: *) - (* TODO: This causes inconsistent results: - interval not sufficiently refined: - inv_bin_int: unequal: (Unknown int([-31,31]),[0,1]) and (0,[0,0]); ikind: int; a': (Not {0}([-31,31]),[-2147483648,2147483647]), b': (0,[0,0]) - binop: m == 0, a': (Not {0}([-31,31]),[0,1]), b': (0,[0,0]) *) - let excl a b = match ID.to_int a with Some x -> ID.of_excl_list ikind [x] | None -> b in - let a' = excl b a in - let b' = excl a b in - if M.tracing then M.tracel "inv" "inv_bin_int: unequal: %a and %a; ikind: %a; a': %a, b': %a\n" ID.pretty a ID.pretty b d_ikind ikind ID.pretty a' ID.pretty b'; - meet_bin a' b' - | _, _ -> a, b + | Eq, Some true + | Ne, Some false -> both m (* def. equal: if they compare equal, both values must be from the meet *) + | Eq, Some false + | Ne, Some true -> (* def. unequal *) + (* Both values can not be in the meet together, but it's not sound to exclude the meet from both. + * e.g. a=[0,1], b=[1,2], meet a b = [1,1], but (a != b) does not imply a=[0,0], b=[2,2] since others are possible: a=[1,1], b=[2,2] + * Only if a is a definite value, we can exclude it from b: *) + (* TODO: This causes inconsistent results: + interval not sufficiently refined: + inv_bin_int: unequal: (Unknown int([-31,31]),[0,1]) and (0,[0,0]); ikind: int; a': (Not {0}([-31,31]),[-2147483648,2147483647]), b': (0,[0,0]) + binop: m == 0, a': (Not {0}([-31,31]),[0,1]), b': (0,[0,0]) *) + let excl a b = match ID.to_int a with Some x -> ID.of_excl_list ikind [x] | None -> b in + let a' = excl b a in + let b' = excl a b in + if M.tracing then M.tracel "inv" "inv_bin_int: unequal: %a and %a; ikind: %a; a': %a, b': %a\n" ID.pretty a ID.pretty b d_ikind ikind ID.pretty a' ID.pretty b'; + meet_bin a' b' + | _, _ -> a, b ) | Lt | Le | Ge | Gt as op -> let pred x = BI.sub x BI.one in let succ x = BI.add x BI.one in (match ID.minimal a, ID.maximal a, ID.minimal b, ID.maximal b with - | Some l1, Some u1, Some l2, Some u2 -> - (* if M.tracing then M.tracel "inv" "Op: %s, l1: %Ld, u1: %Ld, l2: %Ld, u2: %Ld\n" (show_binop op) l1 u1 l2 u2; *) - (* TODO: This causes inconsistent results: - def_exc and interval in conflict: - binop m < 0 with (Not {-1}([-31,31]),[-1,0]) < (0,[0,0]) == (1,[1,1]) - binop: m < 0, a': (Not {-1, 0}([-31,31]),[-1,-1]), b': (0,[0,0]) *) - (match op, ID.to_bool c with + | Some l1, Some u1, Some l2, Some u2 -> + (* if M.tracing then M.tracel "inv" "Op: %s, l1: %Ld, u1: %Ld, l2: %Ld, u2: %Ld\n" (show_binop op) l1 u1 l2 u2; *) + (* TODO: This causes inconsistent results: + def_exc and interval in conflict: + binop m < 0 with (Not {-1}([-31,31]),[-1,0]) < (0,[0,0]) == (1,[1,1]) + binop: m < 0, a': (Not {-1, 0}([-31,31]),[-1,-1]), b': (0,[0,0]) *) + (match op, ID.to_bool c with | Le, Some true | Gt, Some false -> meet_bin (ID.ending ikind u2) (ID.starting ikind l1) | Ge, Some true @@ -297,7 +297,7 @@ struct | Gt, Some true | Le, Some false -> meet_bin (ID.starting ikind (succ l2)) (ID.ending ikind (pred u1)) | _, _ -> a, b) - | _ -> a, b) + | _ -> a, b) | BOr | BXor as op-> if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; (* Be careful: inv_exp performs a meet on both arguments of the BOr / BXor. *) @@ -315,17 +315,17 @@ struct let open Stdlib in let meet_bin a' b' = fd_meet_down ~old:a ~c:a', fd_meet_down ~old:b ~c:b' in (* Refining the abstract values based on branching is roughly based on the idea in [Symbolic execution of floating-point computations](https://hal.inria.fr/inria-00540299/document) - However, their approach is only applicable to the "nearest" rounding mode. Here we use a more general approach covering all possible rounding modes and therefore - use the actual `pred c_min`/`succ c_max` for the outer-bounds instead of the middles between `c_min` and `pred c_min`/`c_max` and `succ c_max` as suggested in the paper. - This also removes the necessity of computing those expressions with higher precise than in the concrete. + However, their approach is only applicable to the "nearest" rounding mode. Here we use a more general approach covering all possible rounding modes and therefore + use the actual `pred c_min`/`succ c_max` for the outer-bounds instead of the middles between `c_min` and `pred c_min`/`c_max` and `succ c_max` as suggested in the paper. + This also removes the necessity of computing those expressions with higher precise than in the concrete. *) try match op with | PlusA -> (* A + B = C, \forall a \in A. a + b_min > pred c_min \land a + b_max < succ c_max \land a + b_max > pred c_min \land a + b_min < succ c_max - \rightarrow A = [min(pred c_min - b_min, pred c_min - b_max), max(succ c_max - b_max, succ c_max - b_min)] - \rightarrow A = [pred c_min - b_max, succ c_max - b_min] + \rightarrow A = [min(pred c_min - b_min, pred c_min - b_max), max(succ c_max - b_max, succ c_max - b_min)] + \rightarrow A = [pred c_min - b_max, succ c_max - b_min] *) let reverse_add v v' = (match FD.minimal c, FD.maximal c, FD.minimal v, FD.maximal v with | Some c_min, Some c_max, Some v_min, Some v_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> @@ -337,8 +337,8 @@ struct | MinusA -> (* A - B = C \ forall a \in A. a - b_max > pred c_min \land a - b_min < succ c_max \land a - b_min > pred c_min \land a - b_max < succ c_max - \rightarrow A = [min(pred c_min + b_max, pred c_min + b_min), max(succ c_max + b_max, succ c_max + b_max)] - \rightarrow A = [pred c_min + b_min, succ c_max + b_max] + \rightarrow A = [min(pred c_min + b_max, pred c_min + b_min), max(succ c_max + b_max, succ c_max + b_max)] + \rightarrow A = [pred c_min + b_min, succ c_max + b_max] *) let a' = (match FD.minimal c, FD.maximal c, FD.minimal b, FD.maximal b with | Some c_min, Some c_max, Some b_min, Some b_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> @@ -348,8 +348,8 @@ struct | _ -> a) in (* A - B = C \ forall b \in B. a_min - b > pred c_min \land a_max - b < succ c_max \land a_max - b > pred c_min \land a_min - b < succ c_max - \rightarrow B = [min(a_max - succ c_max, a_min - succ c_max), max(a_min - pred c_min, a_max - pred c_min)] - \rightarrow B = [a_min - succ c_max, a_max - pred c_min] + \rightarrow B = [min(a_max - succ c_max, a_min - succ c_max), max(a_min - pred c_min, a_max - pred c_min)] + \rightarrow B = [a_min - succ c_max, a_max - pred c_min] *) let b' = (match FD.minimal c, FD.maximal c, FD.minimal a, FD.maximal a with | Some c_min, Some c_max, Some a_min, Some a_max when Float.is_finite (Float.pred c_min) && Float.is_finite (Float.succ c_max) -> @@ -360,9 +360,9 @@ struct meet_bin a' b' | Mult -> (* A * B = C \forall a \in A, a > 0. a * b_min > pred c_min \land a * b_max < succ c_max - A * B = C \forall a \in A, a < 0. a * b_max > pred c_min \land a * b_min < succ c_max - (with negative b reversed <>) - \rightarrow A = [min(pred c_min / b_min, pred c_min / b_max, succ c_max / b_min, succ c_max /b_max), + A * B = C \forall a \in A, a < 0. a * b_max > pred c_min \land a * b_min < succ c_max + (with negative b reversed <>) + \rightarrow A = [min(pred c_min / b_min, pred c_min / b_max, succ c_max / b_min, succ c_max /b_max), max(succ c_max / b_min, succ c_max /b_max, pred c_min / b_min, pred c_min / b_max)] *) let reverse_mul v v' = (match FD.minimal c, FD.maximal c, FD.minimal v, FD.maximal v with @@ -375,11 +375,11 @@ struct meet_bin (reverse_mul b a) (reverse_mul a b) | Div -> (* A / B = C \forall a \in A, a > 0, b_min > 1. a / b_max > pred c_min \land a / b_min < succ c_max - A / B = C \forall a \in A, a < 0, b_min > 1. a / b_min > pred c_min \land a / b_max < succ c_max - A / B = C \forall a \in A, a > 0, 0 < b_min, b_max < 1. a / b_max > pred c_min \land a / b_min < succ c_max - A / B = C \forall a \in A, a < 0, 0 < b_min, b_max < 1. a / b_min > pred c_min \land a / b_max < succ c_max - ... same for negative b - \rightarrow A = [min(b_max * pred c_min, b_min * pred c_min, b_min * succ c_max, b_max * succ c_max), + A / B = C \forall a \in A, a < 0, b_min > 1. a / b_min > pred c_min \land a / b_max < succ c_max + A / B = C \forall a \in A, a > 0, 0 < b_min, b_max < 1. a / b_max > pred c_min \land a / b_min < succ c_max + A / B = C \forall a \in A, a < 0, 0 < b_min, b_max < 1. a / b_min > pred c_min \land a / b_max < succ c_max + ... same for negative b + \rightarrow A = [min(b_max * pred c_min, b_min * pred c_min, b_min * succ c_max, b_max * succ c_max), max(b_max * succ c_max, b_min * succ c_max, b_max * pred c_min, b_min * pred c_min)] *) let a' = (match FD.minimal c, FD.maximal c, FD.minimal b, FD.maximal b with @@ -391,9 +391,9 @@ struct | _ -> a) in (* A / B = C \forall b \in B, b > 0, a_min / b > pred c_min \land a_min / b < succ c_max \land a_max / b > pred c_min \land a_max / b < succ c_max - A / B = C \forall b \in B, b < 0, a_min / b > pred c_min \land a_min / b < succ c_max + A / B = C \forall b \in B, b < 0, a_min / b > pred c_min \land a_min / b < succ c_max \land a_max / b > pred c_min \land a_max / b < succ c_max - \rightarrow (b != 0) B = [min(a_min / succ c_max, a_max / succ c_max, a_min / pred c_min, a_max / pred c_min), + \rightarrow (b != 0) B = [min(a_min / succ c_max, a_max / succ c_max, a_min / pred c_min, a_max / pred c_min), max(a_min / pred c_min, a_max / pred c_min, a_min / succ c_max, a_max / succ c_max)] *) let b' = (match FD.minimal c, FD.maximal c, FD.minimal a, FD.maximal a with @@ -407,18 +407,18 @@ struct | Eq | Ne as op -> let both x = x, x in (match op, ID.to_bool (FD.to_int IBool c) with - | Eq, Some true - | Ne, Some false -> both (FD.meet a b) (* def. equal: if they compare equal, both values must be from the meet *) - | Eq, Some false - | Ne, Some true -> (* def. unequal *) - (* M.debug ~category:Analyzer "Can't use unequal information about float value in expression \"%a\"." d_plainexp exp; *) - a, b (* TODO: no meet_bin? *) - | _, _ -> a, b + | Eq, Some true + | Ne, Some false -> both (FD.meet a b) (* def. equal: if they compare equal, both values must be from the meet *) + | Eq, Some false + | Ne, Some true -> (* def. unequal *) + (* M.debug ~category:Analyzer "Can't use unequal information about float value in expression \"%a\"." d_plainexp exp; *) + a, b (* TODO: no meet_bin? *) + | _, _ -> a, b ) | Lt | Le | Ge | Gt as op -> (match FD.minimal a, FD.maximal a, FD.minimal b, FD.maximal b with - | Some l1, Some u1, Some l2, Some u2 -> - (match op, ID.to_bool (FD.to_int IBool c) with + | Some l1, Some u1, Some l2, Some u2 -> + (match op, ID.to_bool (FD.to_int IBool c) with | Le, Some true | Gt, Some false -> meet_bin (FD.ending (FD.get_fkind a) u2) (FD.starting (FD.get_fkind b) l1) | Ge, Some true @@ -428,7 +428,7 @@ struct | Gt, Some true | Le, Some false -> meet_bin (FD.starting_after (FD.get_fkind a) l2) (FD.ending_before (FD.get_fkind b) u1) | _, _ -> a, b) - | _ -> a, b) + | _ -> a, b) | op -> if M.tracing then M.tracel "inv" "Unhandled operator %a\n" d_binop op; a, b @@ -462,24 +462,24 @@ struct let invert_binary_op c pretty c_int c_float = if M.tracing then M.tracel "inv" "binop %a with %a %a %a == %a\n" d_exp e VD.pretty (eval e1 st) d_binop op VD.pretty (eval e2 st) pretty c; (match eval e1 st, eval e2 st with - | `Int a, `Int b -> - let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) - let a', b' = inv_bin_int (a, b) ikind (c_int ikind) op in - if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; - let st' = inv_exp (`Int a') e1 st in - let st'' = inv_exp (`Int b') e2 st' in - st'' - | `Float a, `Float b -> - let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) - let a', b' = inv_bin_float (a, b) (c_float fkind) op in - if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; - let st' = inv_exp (`Float a') e1 st in - let st'' = inv_exp (`Float b') e2 st' in - st'' - (* Mixed `Float and `Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) - | `Int _, `Float _ | `Float _, `Int _ -> failwith "ill-typed program"; - (* | `Address a, `Address b -> ... *) - | a1, a2 -> fallback ("binop: got abstract values that are not `Int: " ^ sprint VD.pretty a1 ^ " and " ^ sprint VD.pretty a2) st) + | `Int a, `Int b -> + let ikind = Cilfacade.get_ikind_exp e1 in (* both operands have the same type (except for Shiftlt, Shiftrt)! *) + let a', b' = inv_bin_int (a, b) ikind (c_int ikind) op in + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e ID.pretty (c_int ikind) ID.pretty a' ID.pretty b'; + let st' = inv_exp (`Int a') e1 st in + let st'' = inv_exp (`Int b') e2 st' in + st'' + | `Float a, `Float b -> + let fkind = Cilfacade.get_fkind_exp e1 in (* both operands have the same type *) + let a', b' = inv_bin_float (a, b) (c_float fkind) op in + if M.tracing then M.tracel "inv" "binop: %a, c: %a, a': %a, b': %a\n" d_exp e FD.pretty (c_float fkind) FD.pretty a' FD.pretty b'; + let st' = inv_exp (`Float a') e1 st in + let st'' = inv_exp (`Float b') e2 st' in + st'' + (* Mixed `Float and `Int cases should never happen, as there are no binary operators with one float and one int parameter ?!*) + | `Int _, `Float _ | `Float _, `Int _ -> failwith "ill-typed program"; + (* | `Address a, `Address b -> ... *) + | a1, a2 -> fallback ("binop: got abstract values that are not `Int: " ^ sprint VD.pretty a1 ^ " and " ^ sprint VD.pretty a2) st) (* use closures to avoid unused casts *) in (match c_typed with | `Int c -> invert_binary_op c ID.pretty (fun ik -> ID.cast_to ik c) (fun fk -> FD.of_int fk c) @@ -490,46 +490,46 @@ struct let update_lval c x c' pretty = refine_lv ctx a gs st c x c' pretty exp in let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) (match c_typed with - | `Int c -> update_lval c x (match t with - | TPtr _ -> `Address (AD.of_int (module ID) c) - | TInt (ik, _) - | TEnum ({ekind = ik; _}, _) -> `Int (ID.cast_to ik c) - | TFloat (fk, _) -> `Float (FD.of_int fk c) - | _ -> `Int c) ID.pretty - | `Float c -> update_lval c x (match t with - (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) - | TInt (ik, _) -> `Int (FD.to_int ik c) - (* this is theoretically possible and should be handled correctly, however i can't imagine an actual piece of c code producing this?! *) - | TEnum ({ekind = ik; _}, _) -> `Int (FD.to_int ik c) - | TFloat (fk, _) -> `Float (FD.cast_to fk c) - | _ -> `Float c) FD.pretty - | _ -> failwith "unreachable") + | `Int c -> update_lval c x (match t with + | TPtr _ -> `Address (AD.of_int (module ID) c) + | TInt (ik, _) + | TEnum ({ekind = ik; _}, _) -> `Int (ID.cast_to ik c) + | TFloat (fk, _) -> `Float (FD.of_int fk c) + | _ -> `Int c) ID.pretty + | `Float c -> update_lval c x (match t with + (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) + | TInt (ik, _) -> `Int (FD.to_int ik c) + (* this is theoretically possible and should be handled correctly, however i can't imagine an actual piece of c code producing this?! *) + | TEnum ({ekind = ik; _}, _) -> `Int (FD.to_int ik c) + | TFloat (fk, _) -> `Float (FD.cast_to fk c) + | _ -> `Float c) FD.pretty + | _ -> failwith "unreachable") | Const _ , _ -> st (* nothing to do *) | CastE ((TFloat (_, _)), e), `Float c -> (match Cilfacade.typeOf e, FD.get_fkind c with - | TFloat (FLongDouble as fk, _), FFloat - | TFloat (FDouble as fk, _), FFloat - | TFloat (FLongDouble as fk, _), FDouble - | TFloat (fk, _), FLongDouble - | TFloat (FDouble as fk, _), FDouble - | TFloat (FFloat as fk, _), FFloat -> inv_exp (`Float (FD.cast_to fk c)) e st - | _ -> fallback ("CastE: incompatible types") st) + | TFloat (FLongDouble as fk, _), FFloat + | TFloat (FDouble as fk, _), FFloat + | TFloat (FLongDouble as fk, _), FDouble + | TFloat (fk, _), FLongDouble + | TFloat (FDouble as fk, _), FDouble + | TFloat (FFloat as fk, _), FFloat -> inv_exp (`Float (FD.cast_to fk c)) e st + | _ -> fallback ("CastE: incompatible types") st) | CastE ((TInt (ik, _)) as t, e), `Int c | CastE ((TEnum ({ekind = ik; _ }, _)) as t, e), `Int c -> (* Can only meet the t part of an Lval in e with c (unless we meet with all overflow possibilities)! Since there is no good way to do this, we only continue if e has no values outside of t. *) (match eval e st with - | `Int i -> - if ID.leq i (ID.cast_to ik i) then - match Cilfacade.typeOf e with - | TInt(ik_e, _) - | TEnum ({ekind = ik_e; _ }, _) -> - (* let c' = ID.cast_to ik_e c in *) - let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) - if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; - inv_exp (`Int c') e st - | x -> fallback ("CastE: e did evaluate to `Int, but the type did not match" ^ sprint d_type t) st - else - fallback ("CastE: " ^ sprint d_plainexp e ^ " evaluates to " ^ sprint ID.pretty i ^ " which is bigger than the type it is cast to which is " ^ sprint d_type t) st - | v -> fallback ("CastE: e did not evaluate to `Int, but " ^ sprint VD.pretty v) st) + | `Int i -> + if ID.leq i (ID.cast_to ik i) then + match Cilfacade.typeOf e with + | TInt(ik_e, _) + | TEnum ({ekind = ik_e; _ }, _) -> + (* let c' = ID.cast_to ik_e c in *) + let c' = ID.cast_to ik_e (ID.meet c (ID.cast_to ik (ID.top_of ik_e))) in (* TODO: cast without overflow, is this right for normal invariant? *) + if M.tracing then M.tracel "inv" "cast: %a from %a to %a: i = %a; cast c = %a to %a = %a\n" d_exp e d_ikind ik_e d_ikind ik ID.pretty i ID.pretty c d_ikind ik_e ID.pretty c'; + inv_exp (`Int c') e st + | x -> fallback ("CastE: e did evaluate to `Int, but the type did not match" ^ sprint d_type t) st + else + fallback ("CastE: " ^ sprint d_plainexp e ^ " evaluates to " ^ sprint ID.pretty i ^ " which is bigger than the type it is cast to which is " ^ sprint d_type t) st + | v -> fallback ("CastE: e did not evaluate to `Int, but " ^ sprint VD.pretty v) st) | e, _ -> fallback (sprint d_plainexp e ^ " not implemented") st in if eval_bool exp st = Some (not tv) then raise Analyses.Deadcode (* we already know that the branch is dead *) From 776c1f2078737b5126d2617456e2d3b9b67afc8c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Sep 2022 10:44:23 +0300 Subject: [PATCH 060/132] Add YAML unassume testing on SV-COMP --- conf/svcomp-yaml-validate.json | 75 ++++++++++++++++++ conf/svcomp-yaml.json | 77 +++++++++++++++++++ .../yaml/goblint-validate-tmp.xml | 26 +++++++ .../yaml/goblint-validate.xml | 26 +++++++ sv-comp/my-bench-sv-comp/yaml/goblint.sh | 40 ++++++++++ sv-comp/my-bench-sv-comp/yaml/goblint.xml | 23 ++++++ .../my-bench-sv-comp/yaml/table-generator.xml | 8 ++ 7 files changed, 275 insertions(+) create mode 100644 conf/svcomp-yaml-validate.json create mode 100644 conf/svcomp-yaml.json create mode 100644 sv-comp/my-bench-sv-comp/yaml/goblint-validate-tmp.xml create mode 100644 sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml create mode 100755 sv-comp/my-bench-sv-comp/yaml/goblint.sh create mode 100644 sv-comp/my-bench-sv-comp/yaml/goblint.xml create mode 100644 sv-comp/my-bench-sv-comp/yaml/table-generator.xml diff --git a/conf/svcomp-yaml-validate.json b/conf/svcomp-yaml-validate.json new file mode 100644 index 0000000000..b66de889c7 --- /dev/null +++ b/conf/svcomp-yaml-validate.json @@ -0,0 +1,75 @@ +{ + "ana": { + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "escape", + "expRelation", + "mhp", + "var_eq", + "symb_locks", + "region", + "thread", + "unassume" + ], + "context": { + "widen": false + }, + "malloc": { + "wrappers": [ + "kmalloc", + "__kmalloc", + "usb_alloc_urb", + "__builtin_alloca", + "kzalloc", + + "ldv_malloc", + + "kzalloc_node", + "ldv_zalloc", + "kmalloc_array", + "kcalloc", + + "ldv_xmalloc", + "ldv_xzalloc", + "ldv_calloc" + ] + } + }, + "exp": { + "region-offsets": true + }, + "witness": { + "id": "enumerate", + "unknown": false + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + } + } +} diff --git a/conf/svcomp-yaml.json b/conf/svcomp-yaml.json new file mode 100644 index 0000000000..774a29acf1 --- /dev/null +++ b/conf/svcomp-yaml.json @@ -0,0 +1,77 @@ +{ + "ana": { + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "escape", + "expRelation", + "mhp", + "var_eq", + "symb_locks", + "region", + "thread" + ], + "context": { + "widen": false + }, + "malloc": { + "wrappers": [ + "kmalloc", + "__kmalloc", + "usb_alloc_urb", + "__builtin_alloca", + "kzalloc", + + "ldv_malloc", + + "kzalloc_node", + "ldv_zalloc", + "kmalloc_array", + "kcalloc", + + "ldv_xmalloc", + "ldv_xzalloc", + "ldv_calloc" + ] + } + }, + "exp": { + "region-offsets": true + }, + "witness": { + "id": "enumerate", + "unknown": false, + "yaml": { + "enabled": true + } + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + } + } +} diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint-validate-tmp.xml b/sv-comp/my-bench-sv-comp/yaml/goblint-validate-tmp.xml new file mode 100644 index 0000000000..f936cf298c --- /dev/null +++ b/sv-comp/my-bench-sv-comp/yaml/goblint-validate-tmp.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + evals = (\d+) + + + + + /mnt/goblint-svcomp/benchexec/results/yaml-5-evals/goblint.2022-09-15_14-37-40.files/${rundefinition_name}/${taskdef_name}/witness.yml + + + + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/ReachSafety-Loops-Simple.set + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp + + + + + diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml b/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml new file mode 100644 index 0000000000..3d9738d139 --- /dev/null +++ b/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + evals = (\d+) + + + + + RESULTSDIR/LOGDIR/${rundefinition_name}/${taskdef_name}/witness.yml + + + + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/ReachSafety-Loops-Simple.set + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp + + + + + diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint.sh b/sv-comp/my-bench-sv-comp/yaml/goblint.sh new file mode 100755 index 0000000000..16729c6e23 --- /dev/null +++ b/sv-comp/my-bench-sv-comp/yaml/goblint.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +shopt -s extglob + +MYBENCHDIR=/mnt/goblint-svcomp/benchexec/my-bench-sv-comp/yaml +RESULTSDIR=/mnt/goblint-svcomp/benchexec/results/yaml-5-evals +GOBLINTPARALLEL=14 +VALIDATEPARALLEL=14 + +mkdir $RESULTSDIR + +# Run verification +cd /mnt/goblint-svcomp/sv-comp/goblint +# read-only and overlay dirs for Value too large for defined data type workaround +benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $GOBLINTPARALLEL $MYBENCHDIR/goblint.xml + +# Extract witness directory +cd $RESULTSDIR +LOGDIR=`echo goblint.*.files` +echo $LOGDIR + +# Construct validation XMLs +cd $MYBENCHDIR +sed -e "s|RESULTSDIR|$RESULTSDIR|" -e "s/LOGDIR/$LOGDIR/" goblint-validate.xml > goblint-validate-tmp.xml + +# Run validation +cd /mnt/goblint-svcomp/sv-comp/goblint +benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $VALIDATEPARALLEL $MYBENCHDIR/goblint-validate-tmp.xml + +# Merge witness validation results +cd $RESULTSDIR +python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint.*.results.*.xml.bz2 goblint-validate-tmp.*.results.*.xml.bz2 + +# Generate table with merged results and witness validation results +sed -e "s/LOGDIR/$LOGDIR/" $MYBENCHDIR/table-generator.xml > table-generator.xml +table-generator -x table-generator.xml + +# Decompress all tool outputs for table HTML links +unzip -o goblint.*.logfiles.zip +unzip -o goblint-validate-tmp.*.logfiles.zip diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint.xml b/sv-comp/my-bench-sv-comp/yaml/goblint.xml new file mode 100644 index 0000000000..8ab23ef901 --- /dev/null +++ b/sv-comp/my-bench-sv-comp/yaml/goblint.xml @@ -0,0 +1,23 @@ + + + + + **.yml + + + + + + evals = (\d+) + + + + + + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/ReachSafety-Loops-Simple.set + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp + + + + + diff --git a/sv-comp/my-bench-sv-comp/yaml/table-generator.xml b/sv-comp/my-bench-sv-comp/yaml/table-generator.xml new file mode 100644 index 0000000000..cf5c914beb --- /dev/null +++ b/sv-comp/my-bench-sv-comp/yaml/table-generator.xml @@ -0,0 +1,8 @@ + + + + + + + +
From 7283b35834a549d0ab7a5b022826f78caeea3f1d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Sep 2022 10:46:34 +0300 Subject: [PATCH 061/132] Add option witness.invariant.exact --- conf/svcomp-yaml.json | 3 ++ src/analyses/apron/apronAnalysis.apron.ml | 1 + src/analyses/varEq.ml | 2 +- src/cdomains/intDomain.ml | 38 ++++++++++++++++------- src/util/options.schema.json | 6 ++++ 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/conf/svcomp-yaml.json b/conf/svcomp-yaml.json index 774a29acf1..9c4442b806 100644 --- a/conf/svcomp-yaml.json +++ b/conf/svcomp-yaml.json @@ -63,6 +63,9 @@ "unknown": false, "yaml": { "enabled": true + }, + "invariant": { + "exact": false } }, "solver": "td3", diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index f8f1b670a7..3a397a0828 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -572,6 +572,7 @@ struct let vf' x = vf (Obj.repr x) in Priv.iter_sys_vars ctx.global vq vf' | Queries.Invariant context -> + (* TODO: witness.invariant.exact *) query_invariant ctx context | _ -> Result.top q diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 4df6f92528..f33219a07f 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -586,7 +586,7 @@ struct let r = eq_set_clos e ctx.local in if M.tracing then M.tracel "var_eq" "equalset %a = %a\n" d_plainexp e Queries.ES.pretty r; r - | Queries.Invariant context -> + | Queries.Invariant context when GobConfig.get_bool "witness.invariant.exact" -> (* only exact equalities here *) let scope = Node.find_fundec ctx.node in D.invariant ~scope ctx.local | _ -> Queries.Result.top x diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 9f6350e3d3..baa79a5996 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -842,8 +842,11 @@ struct let invariant_ikind e ik x = match x with | Some (x1, x2) when Ints_t.compare x1 x2 = 0 -> - let x1 = Ints_t.to_bigint x1 in - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x1, intType)) + if get_bool "witness.invariant.exact" then + let x1 = Ints_t.to_bigint x1 in + Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x1, intType)) + else + Invariant.top () | Some (x1, x2) -> let open Invariant in let (min_ik, max_ik) = range ik in @@ -1615,7 +1618,11 @@ struct let invariant_ikind e ik (x:t) = match x with - | `Definite x -> Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) + | `Definite x -> + if get_bool "witness.invariant.exact" then + Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) + else + Invariant.top () | `Excluded (s, _) -> S.fold (fun x a -> let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in @@ -2018,10 +2025,13 @@ module Enums : S with type int_t = BigInt.t = struct let invariant_ikind e ik x = match x with | Inc ps -> - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in - Invariant.(a || i) - ) (Invariant.bot ()) (BISet.elements ps) + if BISet.cardinal ps > 1 || get_bool "witness.invariant.exact" then + List.fold_left (fun a x -> + let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in + Invariant.(a || i) + ) (Invariant.bot ()) (BISet.elements ps) + else + Invariant.top () | Exc (ns, _) -> List.fold_left (fun a x -> let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in @@ -2470,8 +2480,11 @@ struct let invariant_ikind e ik x = match x with | Some (c, m) when m =: Ints_t.zero -> - let c = Ints_t.to_bigint c in - Invariant.of_exp Cil.(BinOp (Eq, e, Cil.kintegerCilint ik c, intType)) + if get_bool "witness.invariant.exact" then + let c = Ints_t.to_bigint c in + Invariant.of_exp Cil.(BinOp (Eq, e, Cil.kintegerCilint ik c, intType)) + else + Invariant.top () | Some (c, m) -> let open Cil in let (c, m) = BatTuple.Tuple2.mapn (fun a -> kintegerCilint ik @@ Ints_t.to_bigint a) (c, m) in @@ -2908,8 +2921,11 @@ module IntDomTupleImpl = struct let invariant_ikind e ik x = match to_int x with | Some v -> - (* If definite, output single equality instead of every subdomain repeating same equality *) - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik v, intType)) + if get_bool "witness.invariant.exact" then + (* If definite, output single equality instead of every subdomain repeating same equality *) + Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik v, intType)) + else + Invariant.top () | None -> let is = to_list (mapp { fp = fun (type a) (module I:S with type t = a) -> I.invariant_ikind e ik } x) in List.fold_left (fun a i -> diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 9b26f2275c..0b9b01a31c 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -2039,6 +2039,12 @@ "Whether to dump assertions about all local variables or limitting it to modified ones where possible.", "type": "boolean", "default": true + }, + "exact": { + "title": "witness.invariant.exact", + "description": "Emit exact invariants. They are useless for unassuming.", + "type": "boolean", + "default": true } }, "additionalProperties": false From 6655115faeb66c84032a765281ba0ce5bc9176c5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Sep 2022 10:48:51 +0300 Subject: [PATCH 062/132] Disable GraphML witnesses in svcomp-yaml confs --- conf/svcomp-yaml-validate.json | 3 +-- conf/svcomp-yaml.json | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/conf/svcomp-yaml-validate.json b/conf/svcomp-yaml-validate.json index b66de889c7..070d6f8ed5 100644 --- a/conf/svcomp-yaml-validate.json +++ b/conf/svcomp-yaml-validate.json @@ -60,8 +60,7 @@ "region-offsets": true }, "witness": { - "id": "enumerate", - "unknown": false + "enabled": false }, "solver": "td3", "sem": { diff --git a/conf/svcomp-yaml.json b/conf/svcomp-yaml.json index 9c4442b806..2ae7487ebd 100644 --- a/conf/svcomp-yaml.json +++ b/conf/svcomp-yaml.json @@ -59,8 +59,7 @@ "region-offsets": true }, "witness": { - "id": "enumerate", - "unknown": false, + "enabled": false, "yaml": { "enabled": true }, From a32bf8fb4f8b77115ec2b200b4fb3adc1d193b75 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Sep 2022 11:11:47 +0300 Subject: [PATCH 063/132] Filter unassume nodes according to options Due to disabled synthetic filtering, this is required not to accidentally unassume a loop head invariant inside the loop. For example in sv-benchmarks/c/loop-simple/nested_1. --- conf/svcomp-yaml-validate.json | 7 ++++++- src/analyses/unassumeAnalysis.ml | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/conf/svcomp-yaml-validate.json b/conf/svcomp-yaml-validate.json index 070d6f8ed5..649168878f 100644 --- a/conf/svcomp-yaml-validate.json +++ b/conf/svcomp-yaml-validate.json @@ -60,7 +60,12 @@ "region-offsets": true }, "witness": { - "enabled": false + "enabled": false, + "invariant": { + "loop-head": true, + "after-lock": false, + "other": false + } }, "solver": "td3", "sem": { diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 709a17fc6d..8c51c75872 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -39,13 +39,16 @@ struct let init _ = locator := Locator.create (); (* TODO: add Locator.clear *) let module Cfg = (val !MyCFG.current_cfg) in + let module WitnessInvariant = WitnessUtil.Invariant (struct let file = !Cilfacade.current_file end) (Cfg) in (* DFS, copied from CfgTools.find_backwards_reachable *) let reachable = NH.create 100 in let rec iter_node node = if not (NH.mem reachable node) then begin NH.replace reachable node (); - Locator.add !locator (Node.location node) node; + (* TODO: filter synthetic like in Validator *) + if WitnessInvariant.is_invariant_node node then + Locator.add !locator (Node.location node) node; List.iter (fun (_, prev_node) -> iter_node prev_node ) (Cfg.prev node) From 8a5c09937b8a80510c3abf1f2797bd4aa91d5603 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Sep 2022 11:15:23 +0300 Subject: [PATCH 064/132] Emit inexact invariants for top intervals --- src/cdomains/intDomain.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index baa79a5996..d7d7457e5d 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -840,20 +840,20 @@ struct else top_bool let invariant_ikind e ik x = + let exact = get_bool "witness.invariant.exact" in match x with | Some (x1, x2) when Ints_t.compare x1 x2 = 0 -> - if get_bool "witness.invariant.exact" then + if exact then let x1 = Ints_t.to_bigint x1 in Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x1, intType)) else Invariant.top () | Some (x1, x2) -> - let open Invariant in let (min_ik, max_ik) = range ik in let (x1', x2') = BatTuple.Tuple2.mapn (Ints_t.to_bigint) (x1, x2) in - let i1 = if Ints_t.compare min_ik x1 <> 0 then of_exp Cil.(BinOp (Le, kintegerCilint ik x1', e, intType)) else none in - let i2 = if Ints_t.compare x2 max_ik <> 0 then of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2', intType)) else none in - i1 && i2 + let i1 = if not exact || Ints_t.compare min_ik x1 <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1', e, intType)) else Invariant.none in + let i2 = if not exact || Ints_t.compare x2 max_ik <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2', intType)) else Invariant.none in + Invariant.(i1 && i2) | None -> Invariant.none let arbitrary ik = From 392b32c4b173b219bf601dc18f5852a6f164225f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Sep 2022 11:44:01 +0300 Subject: [PATCH 065/132] Optimize base unassume queries with cache --- src/analyses/base.ml | 82 ++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f69622c362..8d321ab4d8 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2289,48 +2289,56 @@ struct (* The usual recursion trick for ctx. *) (* Must change ctx used by ask to also use new st (not ctx.local), otherwise recursive EvalInt queries use outdated state. *) (* Note: query is just called on base, but not any other analyses. Potentially imprecise, but seems to be sufficient for now. *) - let rec ctx' asked = + let rec ctx' ~querycache asked = { ctx with - ask = (fun (type a) (q: a Queries.t) -> query' asked q) + ask = (fun (type a) (q: a Queries.t) -> query' ~querycache asked q) ; local } - and query': type a. Queries.Set.t -> a Queries.t -> a Queries.result = fun asked q -> + and query': type a. querycache:Obj.t Queries.Hashtbl.t -> Queries.Set.t -> a Queries.t -> a Queries.result = fun ~querycache asked q -> let anyq = Queries.Any q in - if Queries.Set.mem anyq asked then - Queries.Result.top q (* query cycle *) - else ( - let asked' = Queries.Set.add anyq asked in - match q with - | MustBeSingleThreaded when single -> true - | MayEscape _ - | MayBePublic _ - | MayBePublicWithout _ - | MustBeProtectedBy _ - | MustLockset - | MustBeAtomic - | MustBeSingleThreaded - | MustBeUniqueThread - | CurrentThreadId - | MayBeThreadReturn - | PartAccess _ - | IsHeapVar _ - | IsMultiple _ - | CreatedThreads - | MustJoinedThreads -> - (* These queries are safe to ask from outside, - where base doesn't have the partial top local state. - They are also needed for sensible eval behavior via [inv_exp] - such that everything wouldn't be may escaped. *) - ctx.ask q - | _ -> - (* Other queries are not safe, because they would - query the local value state instead of top. - Therefore, these are answered only by base on the - partial top local state. *) - query (ctx' asked') q + match Queries.Hashtbl.find_option querycache anyq with + | Some r -> Obj.obj r + | None -> + if Queries.Set.mem anyq asked then + Queries.Result.top q (* query cycle *) + else ( + let asked' = Queries.Set.add anyq asked in + let r: a Queries.result = + match q with + | MustBeSingleThreaded when single -> true + | MayEscape _ + | MayBePublic _ + | MayBePublicWithout _ + | MustBeProtectedBy _ + | MustLockset + | MustBeAtomic + | MustBeSingleThreaded + | MustBeUniqueThread + | CurrentThreadId + | MayBeThreadReturn + | PartAccess _ + | IsHeapVar _ + | IsMultiple _ + | CreatedThreads + | MustJoinedThreads -> + (* These queries are safe to ask from outside, + where base doesn't have the partial top local state. + They are also needed for sensible eval behavior via [inv_exp] + such that everything wouldn't be may escaped. *) + ctx.ask q + | _ -> + (* Other queries are not safe, because they would + query the local value state instead of top. + Therefore, these are answered only by base on the + partial top local state. *) + query (ctx' ~querycache asked') q + in + Queries.Hashtbl.replace querycache anyq (Obj.repr r); + r ) in - ctx' Queries.Set.empty + let querycache = Queries.Hashtbl.create 13 in + ctx' ~querycache Queries.Set.empty in let f st = (* TODO: start with empty vars because unassume may unassume values for pointed variables not in the invariant exp *) @@ -2526,7 +2534,7 @@ struct (* TODO: is this type right? *) set ~ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local (eval_lv (Analyses.ask_of_ctx ctx) ctx.global ctx.local lval) (Cilfacade.typeOfLval lval) (`Thread (ValueDomain.Threads.singleton tid)) | Events.Unassume {exp; uuids} -> - unassume ctx exp uuids + Stats.time "base unassume" (unassume ctx exp) uuids | _ -> ctx.local end From a99b51dd57679e6a7806d1ffe9f9781abecf9d1b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Sep 2022 12:57:02 +0300 Subject: [PATCH 066/132] Benchmark Zarith power of 2 --- bench/zarith/benchZarith.ml | 47 +++++++++++++++++++++++++++++++++++++ bench/zarith/dune | 4 ++++ 2 files changed, 51 insertions(+) create mode 100644 bench/zarith/benchZarith.ml create mode 100644 bench/zarith/dune diff --git a/bench/zarith/benchZarith.ml b/bench/zarith/benchZarith.ml new file mode 100644 index 0000000000..e76048bb0f --- /dev/null +++ b/bench/zarith/benchZarith.ml @@ -0,0 +1,47 @@ +(* dune exec bench/zarith/benchZarith.exe -- -a *) + +open Benchmark +open Benchmark.Tree + + +let () = + let pow2_pow n = Big_int_Z.power_int_positive_int 2 n in + let pow2_lsl n = Big_int_Z.shift_left_big_int Big_int_Z.unit_big_int n in + + + register ( + "pow2" @>>> [ + "8" @> lazy ( + let arg = 8 in + throughputN 1 [ + ("pow", pow2_pow, arg); + ("lsl", pow2_lsl, arg); + ] + ); + "16" @> lazy ( + let arg = 16 in + throughputN 1 [ + ("pow", pow2_pow, arg); + ("lsl", pow2_lsl, arg); + ] + ); + "32" @> lazy ( + let arg = 32 in + throughputN 1 [ + ("pow", pow2_pow, arg); + ("lsl", pow2_lsl, arg); + ] + ); + "64" @> lazy ( + let arg = 64 in + throughputN 1 [ + ("pow", pow2_pow, arg); + ("lsl", pow2_lsl, arg); + ] + ); + ] + ) + + +let () = + run_global () diff --git a/bench/zarith/dune b/bench/zarith/dune new file mode 100644 index 0000000000..ac4b939c51 --- /dev/null +++ b/bench/zarith/dune @@ -0,0 +1,4 @@ +(executable + (name benchZarith) + (optional) ; TODO: for some reason this doesn't work: `dune build` still tries to compile if benchmark missing (https://github.com/ocaml/dune/issues/4065) + (libraries benchmark zarith)) From b82d35611d0f64ccd71d5da7516be94b22aef8bb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Sep 2022 13:07:48 +0300 Subject: [PATCH 067/132] Optimize LocalFixpoint with narrowing reuse --- src/solvers/localFixpoint.ml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/solvers/localFixpoint.ml b/src/solvers/localFixpoint.ml index 03ea9462e8..1c212520a7 100644 --- a/src/solvers/localFixpoint.ml +++ b/src/solvers/localFixpoint.ml @@ -7,16 +7,18 @@ struct let x' = f x in let x'' = D.widen x (D.join x x') in if D.equal x x'' then - narrowing x (* switch to narrowing phase *) + narrowing_reuse x x' (* switch to narrowing phase *) else widening x'' - and narrowing x = - let x' = f x in + and narrowing_reuse x x' = let x'' = D.narrow x x' in if D.equal x x'' then x (* end iteration *) else narrowing x'' + and narrowing x = + let x' = f x in + narrowing_reuse x x' in widening init end From 79f7771fad1f4a492312316e0dc58322d18882bb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Sep 2022 10:05:37 +0300 Subject: [PATCH 068/132] Add TODO about widening token domain equal --- src/util/wideningTokens.ml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index 1565e5989a..c068d2b8e3 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -61,6 +61,12 @@ struct let leq (d1, t1) (d2, t2) = D.leq d1 d2 (* ignore tokens for order *) + (* TODO: TD3 uses equal to check for fixpoint, not leq, + so should we override this to ignore tokens to avoid potentially + unnecessary extra evals? + Would also have to override compare and hash. *) + (* let equal (d1, t1) (d2, t2) = D.equal d1 d2 *) + (* join also joins tokens *) let widen (d1, t1) (d2, t2) = From 3a2ee4286b4e66703a2380bbeeabbc8e23d8cb32 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Sep 2022 10:05:56 +0300 Subject: [PATCH 069/132] Enable Stats call counting --- src/util/cilfacade.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index 772bc27c86..f7308be1d1 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -16,6 +16,7 @@ let init_options () = Mergecil.merge_inlines := get_bool "cil.merge.inlines" let init () = + Stats.countCalls := true; initCIL (); removeBranchingOnConstants := false; lowerConstants := true; From c43cb80331923cd222824cddbbad71ff443b39fd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Sep 2022 10:08:52 +0300 Subject: [PATCH 070/132] Implement var_eq unassume to fix SV-COMP unassuming --- src/analyses/varEq.ml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index f33219a07f..70d6fb41b5 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -591,6 +591,19 @@ struct D.invariant ~scope ctx.local | _ -> Queries.Result.top x + let event ctx e octx = + match e with + | Events.Unassume {exp; _} -> + (* Unassume must forget equalities, + otherwise var_eq may still have a numeric first iteration equality + while base has unassumed, causing unnecessary extra evals. *) + Basetype.CilExp.get_vars exp + |> List.map Cil.var + |> List.fold_left (fun st lv -> + remove (Analyses.ask_of_ctx ctx) lv st + ) ctx.local + | _ -> + ctx.local end let _ = From f2622ef445481bea008eef9b21dbbfb2ed18d585 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Sep 2022 10:39:51 +0300 Subject: [PATCH 071/132] Increase Yaml.to_string buffer size for long SV-COMP task names --- 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 d555351ea2..fe29d9aa10 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -115,7 +115,7 @@ let yaml_entries_to_file yaml_entries file = (* Yaml_unix.to_file_exn file yaml *) (* to_file/to_string uses a fixed-size buffer... *) (* estimate how big it should be + extra in case empty *) - let text = Yaml.to_string_exn ~len:(List.length yaml_entries * 2048 + 2048) yaml in + let text = Yaml.to_string_exn ~len:(List.length yaml_entries * 4096 + 2048) yaml in Batteries.output_file ~filename:(Fpath.to_string file) ~text module Query From 9aa428105a1b53c82165bbe8fbcb4d2ca480ae11 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Sep 2022 11:15:14 +0300 Subject: [PATCH 072/132] Add data to typeOfError-s --- src/util/cilfacade.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/util/cilfacade.ml b/src/util/cilfacade.ml index f7308be1d1..10c8934ba2 100644 --- a/src/util/cilfacade.ml +++ b/src/util/cilfacade.ml @@ -319,8 +319,8 @@ type typeOfError = | RealImag_NonNumerical (** unexpected non-numerical type for argument to __real__/__imag__ *) | StartOf_NonArray (** typeOf: StartOf on a non-array *) | Mem_NonPointer of exp (** typeOfLval: Mem on a non-pointer (exp) *) - | Index_NonArray (** typeOffset: Index on a non-array *) - | Field_NonCompound (** typeOffset: Field on a non-compound *) + | Index_NonArray of exp * typ (** typeOffset: Index on a non-array *) + | Field_NonCompound of fieldinfo * typ (** typeOffset: Field on a non-compound *) exception TypeOfError of typeOfError @@ -330,8 +330,8 @@ let () = Printexc.register_printer (function | RealImag_NonNumerical -> "unexpected non-numerical type for argument to __real__/__imag__" | StartOf_NonArray -> "typeOf: StartOf on a non-array" | Mem_NonPointer exp -> Printf.sprintf "typeOfLval: Mem on a non-pointer (%s)" (CilType.Exp.show exp) - | Index_NonArray -> "typeOffset: Index on a non-array" - | Field_NonCompound -> "typeOffset: Field on a non-compound" + | Index_NonArray (e, typ) -> Printf.sprintf "typeOffset: Index on a non-array (%s, %s)" (CilType.Exp.show e) (CilType.Typ.show typ) + | Field_NonCompound (fi, typ) -> Printf.sprintf "typeOffset: Field on a non-compound (%s, %s)" (CilType.Fieldinfo.show fi) (CilType.Typ.show typ) in Some (Printf.sprintf "Cilfacade.TypeOfError(%s)" msg) | _ -> None (* for other exceptions *) @@ -412,19 +412,19 @@ and typeOffset basetyp = in function NoOffset -> basetyp - | Index (_, o) -> begin + | Index (e, o) -> begin match unrollType basetyp with TArray (t, _, baseAttrs) -> let elementType = typeOffset t o in blendAttributes baseAttrs elementType - | t -> raise (TypeOfError Index_NonArray) + | t -> raise (TypeOfError (Index_NonArray (e, t))) end | Field (fi, o) -> match unrollType basetyp with TComp (_, baseAttrs) -> let fieldType = typeOffset fi.ftype o in blendAttributes baseAttrs fieldType - | _ -> raise (TypeOfError Field_NonCompound) + | t -> raise (TypeOfError (Field_NonCompound (fi, t))) (** {!Cil.mkCast} using our {!typeOf}. *) From 73df4cac9812c65a3446c7ea53770fa6d45d59d6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Sep 2022 11:18:17 +0300 Subject: [PATCH 073/132] Fix YAML witness precondition invariant TypeOfError-s --- src/cdomains/valueDomain.ml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index d11f5f3c58..c8b45777ea 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -1230,7 +1230,17 @@ struct else Invariant.none in - let i_deref = deref_invariant ~vs ~lval vi offset (Mem c_exp, NoOffset) in + let i_deref = + match Cilfacade.typeOfLval (Var vi, offset) with + | typ -> + (* Address set for a void* variable contains pointers to values of non-void type, + so insert pointer cast to make invariant expression valid (no field/index on void). *) + let newt = TPtr (typ, []) in + let c_exp = Cilfacade.mkCast ~e:c_exp ~newt in + deref_invariant ~vs ~lval vi offset (Mem c_exp, NoOffset) + | exception Cilfacade.TypeOfError _ -> (* typeOffset: Index on a non-array on calloc-ed alloc variables *) + Invariant.none + in Some (Invariant.(acc || (i && i_deref))) | Addr.NullPtr -> From 672d8a490abff9ffcdd81b8e4f95581c4cd238fa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Sep 2022 11:49:52 +0300 Subject: [PATCH 074/132] Add eloc fallback to loc Fixes locations for CIL-inserted array initializer loops heads. --- src/framework/cfgTools.ml | 8 ++++---- src/util/cilfacade0.ml | 16 +++++++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/framework/cfgTools.ml b/src/framework/cfgTools.ml index aae9831331..21e78fd17f 100644 --- a/src/framework/cfgTools.ml +++ b/src/framework/cfgTools.ml @@ -265,8 +265,8 @@ let createCFG (file: file) = | Instr instrs -> (* non-empty Instr *) let edge_of_instr = function - | Set (lval,exp,loc,eloc) -> eloc, Assign (lval, exp) (* TODO: eloc loc fallback if unknown here and If *) - | Call (lval,func,args,loc,eloc) -> eloc, Proc (lval,func,args) + | Set (lval,exp,loc,eloc) -> Cilfacade.eloc_fallback ~eloc ~loc, Assign (lval, exp) + | Call (lval,func,args,loc,eloc) -> Cilfacade.eloc_fallback ~eloc ~loc, Proc (lval,func,args) | Asm (attr,tmpl,out,inp,regs,loc) -> loc, ASM (tmpl,out,inp) | VarDecl (v, loc) -> loc, VDecl(v) in @@ -290,8 +290,8 @@ let createCFG (file: file) = | [same_stmt] -> (same_stmt, same_stmt) | _ -> failwith "MyCFG.createCFG: invalid number of If succs" in - addEdge (Statement stmt) (eloc, Test (exp, true )) (Statement true_stmt); - addEdge (Statement stmt) (eloc, Test (exp, false)) (Statement false_stmt) + addEdge (Statement stmt) (Cilfacade.eloc_fallback ~eloc ~loc, Test (exp, true )) (Statement true_stmt); + addEdge (Statement stmt) (Cilfacade.eloc_fallback ~eloc ~loc, Test (exp, false)) (Statement false_stmt) | Loop (_, loc, eloc, Some cont, Some brk) -> (* TODO: use loc for something? *) (* CIL already converts Loop logic to Gotos and If. *) diff --git a/src/util/cilfacade0.ml b/src/util/cilfacade0.ml index 80b21d8592..35fd5fb706 100644 --- a/src/util/cilfacade0.ml +++ b/src/util/cilfacade0.ml @@ -19,10 +19,16 @@ let rec get_labelsLoc = function (** Following functions are similar to [Cil] versions, but return expression location instead of entire statement location, where possible. *) (* Ideally we would have both copies of the functions available, but UpdateCil would have to be adapted per-stmtkind/instr to store and update either one or two locations. *) +let eloc_fallback ~eloc ~loc = + if eloc.line < 0 then (* unknown *) + loc + else + eloc + (** Get expression location for [Cil.instr]. *) let get_instrLoc = function - | Set (_, _, _loc, eloc) -> eloc - | Call (_, _, _, _loc, eloc) -> eloc + | Set (_, _, loc, eloc) -> eloc_fallback ~eloc ~loc + | Call (_, _, _, loc, eloc) -> eloc_fallback ~eloc ~loc | Asm (_, _, _, _, _, loc) -> loc | VarDecl (_, loc) -> loc @@ -41,7 +47,7 @@ let rec get_stmtLoc stmt = | ComputedGoto (_, loc) -> loc | Break loc -> loc | Continue loc -> loc - | If (_, _, _, _loc, eloc) -> eloc - | Switch (_, _, _, _loc, eloc) -> eloc - | Loop (_, _loc, eloc, _, _) -> eloc + | If (_, _, _, loc, eloc) -> eloc_fallback ~eloc ~loc + | Switch (_, _, _, loc, eloc) -> eloc_fallback ~eloc ~loc + | Loop (_, loc, eloc, _, _) -> eloc_fallback ~eloc ~loc | Block {bstmts = hd :: _; _} -> get_stmtLoc hd From baf2497591279c08fa4bdc09c909c3ff0c99be76 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Sep 2022 12:05:09 +0300 Subject: [PATCH 075/132] Handle witness.invariant.exact in base address sets --- src/cdomains/valueDomain.ml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cdomains/valueDomain.ml b/src/cdomains/valueDomain.ml index c8b45777ea..1812c151e2 100644 --- a/src/cdomains/valueDomain.ml +++ b/src/cdomains/valueDomain.ml @@ -1206,7 +1206,7 @@ struct let rec ad_invariant ~vs ~offset ~lval x = let c_exp = Cil.(Lval (Option.get lval)) in - let i_opt = AD.fold (fun addr acc_opt -> + let is_opt = AD.fold (fun addr acc_opt -> BatOption.bind acc_opt (fun acc -> match addr with | Addr.UnknownPtr -> @@ -1242,7 +1242,7 @@ struct Invariant.none in - Some (Invariant.(acc || (i && i_deref))) + Some (Invariant.(i && i_deref) :: acc) | Addr.NullPtr -> let i = let addr_exp = integer 0 in @@ -1251,15 +1251,16 @@ struct else Invariant.none in - Some (Invariant.(acc || i)) + Some (i :: acc) (* TODO: handle Addr.StrPtr? *) | _ -> None ) - ) x (Some (Invariant.bot ())) + ) x (Some []) in - match i_opt with - | Some i -> i + match is_opt with + | Some [i] -> if get_bool "witness.invariant.exact" then i else Invariant.none + | Some is -> List.fold_left Invariant.(||) (Invariant.bot ()) is | None -> Invariant.none and blob_invariant ~vs ~offset ~lval (v, _, _) = From a9a6a7aeca5045b03df64847e42396819a3111c1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Sep 2022 13:14:47 +0300 Subject: [PATCH 076/132] Add regexes for filtering invariant variable names --- src/domains/invariantCil.ml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/domains/invariantCil.ml b/src/domains/invariantCil.ml index 75323aff61..d3c4c3a1c1 100644 --- a/src/domains/invariantCil.ml +++ b/src/domains/invariantCil.ml @@ -36,9 +36,13 @@ let exp_is_in_scope scope e = (* TODO: detect temporaries created by Cil? *) (* let var_is_tmp {vdescrpure} = not vdescrpure (* doesn't exclude tmp___0 *) *) (* TODO: instead check if vdescr is nonempty? (doesn't cover all cases, e.g. ternary temporary) *) -let tmp_var_regexp = Str.regexp "^\\(tmp\\(___[0-9]+\\)?\\|cond\\|RETURN\\)$" -(* let var_is_tmp {vname; _} = Str.string_match tmp_var_regexp vname 0 *) -let var_is_tmp vi = Option.is_none (Cilfacade.find_original_name vi) +(* TODO: make option for regex cases *) +let tmp_var_regexp = Str.regexp "^\\(tmp\\(___[0-9]+\\)?\\|__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?\\|.*____CPAchecker_TMP_[0-9]+\\|cond\\|RETURN\\|__VERIFIER_assert__cond\\)$" +let varname_is_tmp vname = Str.string_match tmp_var_regexp vname 0 +let var_is_tmp vi = + match Cilfacade.find_original_name vi with + | None -> true + | Some vname -> varname_is_tmp vname class exp_contains_tmp_visitor (acc: bool ref) = object inherit nopCilVisitor as super method! vvrbl (vi: varinfo) = From c01a7a31ee3997b508803e363b6c740c5c90024e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Sep 2022 13:27:14 +0300 Subject: [PATCH 077/132] Fix unassume causing more evals on sv-benchmarks/c/loops-crafted-1/nested5-2 --- src/framework/control.ml | 3 ++- src/util/wideningTokens.ml | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index 1fcd52103c..77b0d27054 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -24,12 +24,13 @@ let spec_module: (module Spec) Lazy.t = lazy ( |> lift (get_bool "ana.sv-comp.enabled") (module WitnessConstraints.PathSensitive3) |> lift (not (get_bool "ana.sv-comp.enabled")) (module PathSensitive2) |> lift (get_bool "ana.dead-code.branches") (module DeadBranchLifter) + |> lift true (module WideningTokens.Lifter) |> lift true (module DeadCodeLifter) |> lift (get_bool "dbg.slice.on") (module LevelSliceLifter) |> lift (get_int "dbg.limit.widen" > 0) (module LimitLifter) |> lift (get_bool "ana.opt.equal" && not (get_bool "ana.opt.hashcons")) (module OptEqual) |> lift (get_bool "ana.opt.hashcons") (module HashconsLifter) - |> lift true (module WideningTokens.Lifter) + (* |> lift true (module WideningTokens.Lifter) *) ) in GobConfig.building_spec := false; Analyses.control_spec_c := (module S1.C); diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index c068d2b8e3..d9019b6650 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -61,6 +61,9 @@ struct let leq (d1, t1) (d2, t2) = D.leq d1 d2 (* ignore tokens for order *) + (* TODO: adapt all order functions? necessary if outermost lifter *) + (* let is_bot (d, t) = D.is_bot d *) + (* TODO: TD3 uses equal to check for fixpoint, not leq, so should we override this to ignore tokens to avoid potentially unnecessary extra evals? From 3375949229a540a1113d0a5cacebc2c107a65e8b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 20 Sep 2022 10:58:13 +0300 Subject: [PATCH 078/132] Add additional LDV invariant variable filters --- src/domains/invariantCil.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domains/invariantCil.ml b/src/domains/invariantCil.ml index d3c4c3a1c1..93982d7d2f 100644 --- a/src/domains/invariantCil.ml +++ b/src/domains/invariantCil.ml @@ -37,7 +37,7 @@ let exp_is_in_scope scope e = (* let var_is_tmp {vdescrpure} = not vdescrpure (* doesn't exclude tmp___0 *) *) (* TODO: instead check if vdescr is nonempty? (doesn't cover all cases, e.g. ternary temporary) *) (* TODO: make option for regex cases *) -let tmp_var_regexp = Str.regexp "^\\(tmp\\(___[0-9]+\\)?\\|__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?\\|.*____CPAchecker_TMP_[0-9]+\\|cond\\|RETURN\\|__VERIFIER_assert__cond\\)$" +let tmp_var_regexp = Str.regexp "^\\(tmp\\(___[0-9]+\\)?\\|__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?\\|.*____CPAchecker_TMP_[0-9]+\\|cond\\|RETURN\\|__VERIFIER_assert__cond\\|__ksymtab_.*\\|\\(ldv_state_variable\\|ldv_timer_state\\|ldv_timer_list\\|ldv_irq_\\(line_\\|data_\\)?[0-9]+\\|ldv_retval\\)_[0-9]+\\)$" let varname_is_tmp vname = Str.string_match tmp_var_regexp vname 0 let var_is_tmp vi = match Cilfacade.find_original_name vi with From d8ee00faeeba07c3ef7e968b7a6aa4ed99caa720 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 20 Sep 2022 11:53:02 +0300 Subject: [PATCH 079/132] Add option witness.invariant.exclude-vars --- conf/svcomp-yaml.json | 12 +++++++++++- src/domains/invariantCil.ml | 14 +++++++++++--- src/util/options.schema.json | 13 +++++++++++++ src/util/server.ml | 1 + 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/conf/svcomp-yaml.json b/conf/svcomp-yaml.json index 2ae7487ebd..f9bc530a13 100644 --- a/conf/svcomp-yaml.json +++ b/conf/svcomp-yaml.json @@ -64,7 +64,17 @@ "enabled": true }, "invariant": { - "exact": false + "exact": false, + "exclude-vars": [ + "tmp\\(___[0-9]+\\)?", + "cond", + "RETURN", + "__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?", + ".*____CPAchecker_TMP_[0-9]+", + "__VERIFIER_assert__cond", + "__ksymtab_.*", + "\\(ldv_state_variable\\|ldv_timer_state\\|ldv_timer_list\\|ldv_irq_\\(line_\\|data_\\)?[0-9]+\\|ldv_retval\\)_[0-9]+" + ] } }, "solver": "td3", diff --git a/src/domains/invariantCil.ml b/src/domains/invariantCil.ml index 93982d7d2f..2e647f6920 100644 --- a/src/domains/invariantCil.ml +++ b/src/domains/invariantCil.ml @@ -33,12 +33,17 @@ let exp_is_in_scope scope e = ignore (visitCilExpr visitor e); !acc +let exclude_vars_regexp = ResettableLazy.from_fun (fun () -> + GobConfig.get_string_list "witness.invariant.exclude-vars" + |> String.concat "\\|" + |> Printf.sprintf "^\\(%s\\)$" + |> Str.regexp + ) + (* TODO: detect temporaries created by Cil? *) (* let var_is_tmp {vdescrpure} = not vdescrpure (* doesn't exclude tmp___0 *) *) (* TODO: instead check if vdescr is nonempty? (doesn't cover all cases, e.g. ternary temporary) *) -(* TODO: make option for regex cases *) -let tmp_var_regexp = Str.regexp "^\\(tmp\\(___[0-9]+\\)?\\|__\\(cil_\\)?tmp_?[0-9]*\\(_[0-9]+\\)?\\|.*____CPAchecker_TMP_[0-9]+\\|cond\\|RETURN\\|__VERIFIER_assert__cond\\|__ksymtab_.*\\|\\(ldv_state_variable\\|ldv_timer_state\\|ldv_timer_list\\|ldv_irq_\\(line_\\|data_\\)?[0-9]+\\|ldv_retval\\)_[0-9]+\\)$" -let varname_is_tmp vname = Str.string_match tmp_var_regexp vname 0 +let varname_is_tmp vname = Str.string_match (ResettableLazy.force exclude_vars_regexp) vname 0 let var_is_tmp vi = match Cilfacade.find_original_name vi with | None -> true @@ -66,3 +71,6 @@ let exp_contains_tmp e = (* TODO: synchronize magic constant with BaseDomain *) let var_is_heap {vname; _} = BatString.starts_with vname "(alloc@" + +let reset_lazy () = + ResettableLazy.reset exclude_vars_regexp diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 0b9b01a31c..25c524ca12 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -2045,6 +2045,19 @@ "description": "Emit exact invariants. They are useless for unassuming.", "type": "boolean", "default": true + }, + "exclude-vars": { + "title": "witness.invariant.exclude-vars", + "description": "OCaml Str regexes for variables to exclude from invariants.", + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "tmp\\(___[0-9]+\\)?", + "cond", + "RETURN" + ] } }, "additionalProperties": false diff --git a/src/util/server.ml b/src/util/server.ml index 13a9cb738a..e8729a02db 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -189,6 +189,7 @@ let analyze ?(reset=false) (s: t) = Serialize.Cache.reset_data AnalysisData); let increment_data, fresh = increment_data s file reparsed in Cilfacade.reset_lazy (); + InvariantCil.reset_lazy (); WideningThresholds.reset_lazy (); IntDomain.reset_lazy (); ApronDomain.reset_lazy (); From 922e91fcd03221f0af868810ed505f7632635aba Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 20 Sep 2022 12:35:25 +0300 Subject: [PATCH 080/132] Emit def_exc range invariant if more precise than ikind range --- src/cdomains/intDomain.ml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index d7d7457e5d..b94daa9ce2 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -1623,11 +1623,20 @@ struct Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) else Invariant.top () - | `Excluded (s, _) -> + | `Excluded (s, r) -> + (* Emit range invariant if tighter than ikind bounds. + This can be more precise than interval, which has been widened. *) + let (rmin, rmax) = (Exclusion.min_of_range r, Exclusion.max_of_range r) in + let (ikmin, ikmax) = + let ikr = size ik in + (Exclusion.min_of_range ikr, Exclusion.max_of_range ikr) + in + let imin = if (* not exact || *) BI.compare ikmin rmin <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik rmin, e, intType)) else Invariant.none in + let imax = if (* not exact || *) BI.compare rmax ikmax <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik rmax, intType)) else Invariant.none in S.fold (fun x a -> let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in Invariant.(a && i) - ) s (Invariant.top ()) + ) s Invariant.(imin && imax) | `Bot -> Invariant.none let arbitrary ik = From 7c3070af64d6ee11142f37bb77a9ddfd43da7806 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Sep 2022 11:32:20 +0300 Subject: [PATCH 081/132] Add bench-yaml confs --- conf/bench-yaml-validate.json | 73 ++++++++++++++++++++++++++++++++ conf/bench-yaml.json | 78 +++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 conf/bench-yaml-validate.json create mode 100644 conf/bench-yaml.json diff --git a/conf/bench-yaml-validate.json b/conf/bench-yaml-validate.json new file mode 100644 index 0000000000..1d11742b48 --- /dev/null +++ b/conf/bench-yaml-validate.json @@ -0,0 +1,73 @@ +{ + "ana": { + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "activated": [ + "expRelation", + "base", + "threadid", + "threadflag", + "threadreturn", + "escape", + "mutexEvents", + "mutex", + "access", + "mallocWrapper", + "mhp", + "assert", + "unassume" + ], + "malloc": { + "wrappers": [ + "kmalloc", + "__kmalloc", + "usb_alloc_urb", + "__builtin_alloca", + "kzalloc", + + "ldv_malloc", + + "kzalloc_node", + "ldv_zalloc", + "kmalloc_array", + "kcalloc", + + "ldv_xmalloc", + "ldv_xzalloc", + "ldv_calloc" + ] + } + }, + "witness": { + "enabled": false, + "invariant": { + "loop-head": true, + "after-lock": true, + "other": false + } + }, + "sem": { + "unknown_function": { + "invalidate": { + "globals": false + }, + "spawn": true + }, + "builtin_unreachable": { + "dead_code": true + }, + "int": { + "signed_overflow": "assume_none" + } + }, + "pre": { + "cppflags": [ + "-DGOBLINT_NO_PTHREAD_ONCE", + "-DGOBLINT_NO_QSORT", + "-DGOBLINT_NO_BSEARCH" + ] + } +} diff --git a/conf/bench-yaml.json b/conf/bench-yaml.json new file mode 100644 index 0000000000..02b6be226c --- /dev/null +++ b/conf/bench-yaml.json @@ -0,0 +1,78 @@ +{ + "ana": { + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "activated": [ + "expRelation", + "base", + "threadid", + "threadflag", + "threadreturn", + "escape", + "mutexEvents", + "mutex", + "access", + "mallocWrapper", + "mhp", + "assert" + ], + "malloc": { + "wrappers": [ + "kmalloc", + "__kmalloc", + "usb_alloc_urb", + "__builtin_alloca", + "kzalloc", + + "ldv_malloc", + + "kzalloc_node", + "ldv_zalloc", + "kmalloc_array", + "kcalloc", + + "ldv_xmalloc", + "ldv_xzalloc", + "ldv_calloc" + ] + } + }, + "witness": { + "enabled": false, + "yaml": { + "enabled": true + }, + "invariant": { + "exact": false, + "exclude-vars": [ + "tmp\\(___[0-9]+\\)?", + "cond", + "RETURN" + ] + } + }, + "sem": { + "unknown_function": { + "invalidate": { + "globals": false + }, + "spawn": true + }, + "builtin_unreachable": { + "dead_code": true + }, + "int": { + "signed_overflow": "assume_none" + } + }, + "pre": { + "cppflags": [ + "-DGOBLINT_NO_PTHREAD_ONCE", + "-DGOBLINT_NO_QSORT", + "-DGOBLINT_NO_BSEARCH" + ] + } +} From ca9db191b26b23d980d20e986f610dfe4887f57b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Sep 2022 11:33:39 +0300 Subject: [PATCH 082/132] Optimize unassume to not emit during postsolving --- src/analyses/unassumeAnalysis.ml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 8c51c75872..7a95e0f717 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -195,16 +195,18 @@ struct | None -> acc ) ctx.local es in - begin match es with - | x :: xs -> - let e = List.fold_left (fun a {exp = b; _} -> Cil.(BinOp (LAnd, a, b, intType))) x.exp xs in + match es with + | x :: xs -> + let e = List.fold_left (fun a {exp = b; _} -> Cil.(BinOp (LAnd, a, b, intType))) x.exp xs in + (* M.info ~category:Witness "unassume invariant: %a" CilType.Exp.pretty e; *) + if not !Goblintutil.postsolving then ( let uuids = x.uuid :: List.map (fun {uuid; _} -> uuid) xs in ctx.emit (Unassume {exp = e; uuids}); List.iter WideningTokens.perform uuids - | [] -> - () - end; - ctx.local + ); + ctx.local + | [] -> + ctx.local let assign ctx lv e = emit_unassume ctx From 3e11774225ae1daa39b919d6ca174094a478fcb4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Sep 2022 15:40:22 +0300 Subject: [PATCH 083/132] Fix widening tokens domain identity causing extra work --- src/util/wideningTokens.ml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index d9019b6650..cdc43a9a19 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -58,17 +58,19 @@ struct let unlift (d, _) = d let lift d = (d, TS.bot ()) - let leq (d1, t1) (d2, t2) = - D.leq d1 d2 (* ignore tokens for order *) + (* Ignore tokens for identity. - (* TODO: adapt all order functions? necessary if outermost lifter *) - (* let is_bot (d, t) = D.is_bot d *) - - (* TODO: TD3 uses equal to check for fixpoint, not leq, + TD3 uses equal to check for fixpoint, not leq, so should we override this to ignore tokens to avoid potentially - unnecessary extra evals? - Would also have to override compare and hash. *) - (* let equal (d1, t1) (d2, t2) = D.equal d1 d2 *) + unnecessary extra work. *) + let equal (d1, t1) (d2, t2) = D.equal d1 d2 + let compare (d1, t1) (d2, t2) = D.compare d1 d2 + let hash (d, t) = D.hash d + + (* Ignore tokens for order. *) + let leq (d1, t1) (d2, t2) = D.leq d1 d2 + let is_bot (d, t) = D.is_bot d + (* TODO: need others? *) (* join also joins tokens *) From e5855ba78980767ea2342d8204066357af012dd4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Oct 2022 14:10:03 +0300 Subject: [PATCH 084/132] Fix BenchZarith indentation --- bench/zarith/benchZarith.ml | 56 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/bench/zarith/benchZarith.ml b/bench/zarith/benchZarith.ml index e76048bb0f..7411b1e499 100644 --- a/bench/zarith/benchZarith.ml +++ b/bench/zarith/benchZarith.ml @@ -11,34 +11,34 @@ let () = register ( "pow2" @>>> [ - "8" @> lazy ( - let arg = 8 in - throughputN 1 [ - ("pow", pow2_pow, arg); - ("lsl", pow2_lsl, arg); - ] - ); - "16" @> lazy ( - let arg = 16 in - throughputN 1 [ - ("pow", pow2_pow, arg); - ("lsl", pow2_lsl, arg); - ] - ); - "32" @> lazy ( - let arg = 32 in - throughputN 1 [ - ("pow", pow2_pow, arg); - ("lsl", pow2_lsl, arg); - ] - ); - "64" @> lazy ( - let arg = 64 in - throughputN 1 [ - ("pow", pow2_pow, arg); - ("lsl", pow2_lsl, arg); - ] - ); + "8" @> lazy ( + let arg = 8 in + throughputN 1 [ + ("pow", pow2_pow, arg); + ("lsl", pow2_lsl, arg); + ] + ); + "16" @> lazy ( + let arg = 16 in + throughputN 1 [ + ("pow", pow2_pow, arg); + ("lsl", pow2_lsl, arg); + ] + ); + "32" @> lazy ( + let arg = 32 in + throughputN 1 [ + ("pow", pow2_pow, arg); + ("lsl", pow2_lsl, arg); + ] + ); + "64" @> lazy ( + let arg = 64 in + throughputN 1 [ + ("pow", pow2_pow, arg); + ("lsl", pow2_lsl, arg); + ] + ); ] ) From b8716cd4e6542595c44bb1784c708a0dee6c1e22 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Oct 2022 16:14:13 +0300 Subject: [PATCH 085/132] Fix 56-witness/23-base-unassume-priv2 by reverting widening token lifter position --- src/framework/control.ml | 5 +++-- src/util/wideningTokens.ml | 9 +++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index 77b0d27054..9af48decc1 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -24,13 +24,14 @@ let spec_module: (module Spec) Lazy.t = lazy ( |> lift (get_bool "ana.sv-comp.enabled") (module WitnessConstraints.PathSensitive3) |> lift (not (get_bool "ana.sv-comp.enabled")) (module PathSensitive2) |> lift (get_bool "ana.dead-code.branches") (module DeadBranchLifter) - |> lift true (module WideningTokens.Lifter) |> lift true (module DeadCodeLifter) |> lift (get_bool "dbg.slice.on") (module LevelSliceLifter) |> lift (get_int "dbg.limit.widen" > 0) (module LimitLifter) |> lift (get_bool "ana.opt.equal" && not (get_bool "ana.opt.hashcons")) (module OptEqual) |> lift (get_bool "ana.opt.hashcons") (module HashconsLifter) - (* |> lift true (module WideningTokens.Lifter) *) + (* Widening tokens must be outside of hashcons, because widening token domain ignores token sets for identity, so hashcons doesn't allow adding tokens. + Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) + |> lift true (module WideningTokens.Lifter) ) in GobConfig.building_spec := false; Analyses.control_spec_c := (module S1.C); diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index cdc43a9a19..aeb89f8786 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -62,7 +62,10 @@ struct TD3 uses equal to check for fixpoint, not leq, so should we override this to ignore tokens to avoid potentially - unnecessary extra work. *) + unnecessary extra work. + + Thus, this domain should not be used inside hashcons lifter, + because it would prevent token sets changing. *) let equal (d1, t1) (d2, t2) = D.equal d1 d2 let compare (d1, t1) (d2, t2) = D.compare d1 d2 let hash (d, t) = D.hash d @@ -116,7 +119,6 @@ struct let context fd = S.context fd % D.unlift - (* TODO: propagate tokens *) let conv (ctx: (D.t, G.t, C.t, V.t) ctx): (S.D.t, S.G.t, S.C.t, S.V.t) ctx = { ctx with local = D.unlift ctx.local ; split = (fun d es -> ctx.split (d, snd ctx.local) es) @@ -132,6 +134,9 @@ struct ) ) in + (* If transfer function exits via exception, then new tokens are forgotten. + There's nowhere to put them to potentially pass them to splits. + Thus, this functor should not be used inside deadcode lifter. *) f d !ts let lift' d ts = (d, ts) From 9313c22a032130f65d00b8bdbfdd3327c5507e6e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Oct 2022 16:36:54 +0300 Subject: [PATCH 086/132] Fix YAML witness accessed lvals crash on sv-benchmarks/c/loops/bubble_sort-1 --- 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 fe29d9aa10..54ff5b8c8b 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -256,7 +256,7 @@ struct Cfg.next n |> BatList.enum |> BatEnum.filter_map (fun (_, next_n) -> - let next_local = NH.find nh next_n in + let next_local = try NH.find nh next_n with Not_found -> Spec.D.bot () in match Query.ask_local_node gh next_n next_local MayAccessed with | `Top -> None | `Lifted _ as es -> Some es) From 6c9a64a30c71c195d09d687c7a12b30f9fb73152 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Oct 2022 16:40:52 +0300 Subject: [PATCH 087/132] Update YAML witness SV-COMP script --- .../yaml/goblint-validate.xml | 12 ++++++++- sv-comp/my-bench-sv-comp/yaml/goblint.sh | 9 ++++--- sv-comp/my-bench-sv-comp/yaml/goblint.xml | 12 ++++++++- .../my-bench-sv-comp/yaml/table-generator.xml | 26 +++++++++++++++++-- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml b/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml index 3d9738d139..f173f3a98c 100644 --- a/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml +++ b/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml @@ -16,9 +16,19 @@ RESULTSDIR/LOGDIR/${rundefinition_name}/${taskdef_name}/witness.yml - + + + + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64-ReachSafety.set + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp + + + + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64Large-ReachSafety.set + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint.sh b/sv-comp/my-bench-sv-comp/yaml/goblint.sh index 16729c6e23..f27daccf69 100755 --- a/sv-comp/my-bench-sv-comp/yaml/goblint.sh +++ b/sv-comp/my-bench-sv-comp/yaml/goblint.sh @@ -3,9 +3,9 @@ shopt -s extglob MYBENCHDIR=/mnt/goblint-svcomp/benchexec/my-bench-sv-comp/yaml -RESULTSDIR=/mnt/goblint-svcomp/benchexec/results/yaml-5-evals -GOBLINTPARALLEL=14 -VALIDATEPARALLEL=14 +RESULTSDIR=/mnt/goblint-svcomp/benchexec/results/yaml-21-software-rebase +GOBLINTPARALLEL=8 +VALIDATEPARALLEL=8 mkdir $RESULTSDIR @@ -29,7 +29,8 @@ benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RES # Merge witness validation results cd $RESULTSDIR -python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint.*.results.*.xml.bz2 goblint-validate-tmp.*.results.*.xml.bz2 +# python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint.*.results.*.xml.bz2 goblint-validate-tmp.*.results.*.xml.bz2 +python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint.*.results.sv-comp_prop-reachsafety.xml.bz2 goblint-validate-tmp.*.results.sv-comp_prop-reachsafety.xml.bz2 # Generate table with merged results and witness validation results sed -e "s/LOGDIR/$LOGDIR/" $MYBENCHDIR/table-generator.xml > table-generator.xml diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint.xml b/sv-comp/my-bench-sv-comp/yaml/goblint.xml index 8ab23ef901..6ab4dfc65a 100644 --- a/sv-comp/my-bench-sv-comp/yaml/goblint.xml +++ b/sv-comp/my-bench-sv-comp/yaml/goblint.xml @@ -13,9 +13,19 @@ - + + + + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64-ReachSafety.set + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp + + + + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64Large-ReachSafety.set + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp diff --git a/sv-comp/my-bench-sv-comp/yaml/table-generator.xml b/sv-comp/my-bench-sv-comp/yaml/table-generator.xml index cf5c914beb..f6d01fc4db 100644 --- a/sv-comp/my-bench-sv-comp/yaml/table-generator.xml +++ b/sv-comp/my-bench-sv-comp/yaml/table-generator.xml @@ -2,7 +2,29 @@ - - + + + + + witness + total: (\d+) + + + + solving +([0-9.]+) s + vars = (\d+) + evals = (\d+) + + + + + + + + + solving +([0-9.]+) s + vars = (\d+) + evals = (\d+) +
From 6c138dc7b981098560b7b0cf23896e2f86a6b796 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Oct 2022 17:20:02 +0300 Subject: [PATCH 088/132] Clean up and document WideningTokens --- src/analyses/apron/apronAnalysis.apron.ml | 2 +- src/analyses/base.ml | 2 +- src/analyses/mCP.ml | 8 ++- src/analyses/unassumeAnalysis.ml | 2 +- src/util/wideningTokens.ml | 86 +++++++++++++---------- 5 files changed, 57 insertions(+), 43 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 3a397a0828..97a2ff1207 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -634,7 +634,7 @@ struct (* TODO: parallel write_global? *) let st = - WideningTokens.with_side_tokens' (WideningTokens.TS.of_list uuids) (fun () -> + WideningTokens.with_side_tokens (WideningTokens.TS.of_list uuids) (fun () -> VH.fold (fun v v_in st -> (* TODO: is this sideg fine? *) write_global ask ctx.global ctx.sideg st v v_in diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 8d321ab4d8..825a6767bd 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2505,7 +2505,7 @@ struct (* Perform actual [set]-s with final unassumed values. This invokes [Priv.write_global], which was suppressed above. *) let e_d' = - WideningTokens.with_side_tokens' (WideningTokens.TS.of_list uuids) (fun () -> + WideningTokens.with_side_tokens (WideningTokens.TS.of_list uuids) (fun () -> CPA.fold (fun x v acc -> let addr: AD.t = AD.from_var_offset (x, `NoOffset) in set (Analyses.ask_of_ctx ctx) ~ctx ~invariant:false ctx.global acc addr x.vtype v diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 0d3bf5f056..8f7eda2f47 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -163,8 +163,14 @@ struct let do_sideg ctx (xs:(V.t * (WideningTokens.TS.t * G.t)) list) = let side_one v dts = let side_one_ts ts d = - WideningTokens.with_side_tokens ts (fun () -> + (* Do side effects with the tokens that were active at the time. + Transfer functions have exited the with_side_token wrappers by now. *) + let old_side_tokens = !WideningTokens.side_tokens in + WideningTokens.side_tokens := ts; + Fun.protect (fun () -> ctx.sideg v @@ fold_left G.join (G.bot ()) d + ) ~finally:(fun () -> + WideningTokens.side_tokens := old_side_tokens ) in iter (uncurry side_one_ts) @@ group_assoc_eq WideningTokens.TS.equal dts diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 7a95e0f717..ccbe2773a1 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -202,7 +202,7 @@ struct if not !Goblintutil.postsolving then ( let uuids = x.uuid :: List.map (fun {uuid; _} -> uuid) xs in ctx.emit (Unassume {exp = e; uuids}); - List.iter WideningTokens.perform uuids + List.iter WideningTokens.add uuids ); ctx.local | [] -> diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index aeb89f8786..721ccff660 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -1,57 +1,58 @@ -module Token = Basetype.RawStrings -module TS = SetDomain.ToppedSet (Token) (struct let topname = "Top" end) +(** Widening tokens are a generic and dynamic mechanism for delaying widening. + All abstract elements carry a set of tokens, which analyses can add into. + Lifted abstract elements are only widened if the token set does not increase, + i.e. adding a widening token delays a widening. + + @see Mihaila, B., Sepp, A. & Simon, A. Widening as Abstract Domain. *) + +(** Widening token. *) +module Token = Basetype.RawStrings (* Change to variant type if need other tokens than witness UUIDs. *) -type handler = Token.t -> unit -let handler: handler ref = ref (fun _ -> failwith "Unhandled token") +(** Widening token set. *) +module TS = SetDomain.ToppedSet (Token) (struct let topname = "Top" end) -let perform t = !handler t +(** Reference to current {!add} implementation. Maintained by {!Lifter}. *) +let add_ref: (Token.t -> unit) ref = ref (fun _ -> failwith "Unhandled token") -let handle ~(with_:handler) f = - let old_handler = !handler in - handler := with_; - Fun.protect ~finally:(fun () -> - handler := old_handler - ) f +(** Add widening token to local state. *) +let add t = !add_ref t +(** Widening tokens added to side effects. + Maintained by {!Lifter} and {!MCP}. *) let side_tokens: TS.t ref = ref (TS.bot ()) +(** [with_side_token t f] adds widening token [t] to all side effects in [f]. *) let with_side_token t f = let old_side_tokens = !side_tokens in side_tokens := TS.add t old_side_tokens; - Fun.protect ~finally:(fun () -> + Fun.protect f ~finally:(fun () -> side_tokens := old_side_tokens - ) f + ) +(** [with_side_tokens ts f] adds widening tokens [ts] to all side effects in [f]. *) let with_side_tokens ts f = - let old_side_tokens = !side_tokens in - side_tokens := ts; - Fun.protect ~finally:(fun () -> - side_tokens := old_side_tokens - ) f - -let with_side_tokens' ts f = let old_side_tokens = !side_tokens in side_tokens := TS.join ts old_side_tokens; - Fun.protect ~finally:(fun () -> + Fun.protect f ~finally:(fun () -> side_tokens := old_side_tokens - ) f + ) -let local_tokens: TS.t ref = ref (TS.bot ()) -let with_local_tokens ts f = - let old_local_tokens = !local_tokens in - local_tokens := ts; - Fun.protect ~finally:(fun () -> - local_tokens := old_local_tokens - ) f +(** Widening tokens in current local state. Maintained by {!Lifter}. *) +let local_tokens: TS.t ref = ref (TS.bot ()) +(** [with_local_side_tokens f] adds all widening tokens from local state to all side effects in [f]. *) let with_local_side_tokens f = with_side_tokens !local_tokens f + open Prelude open Analyses +(** Lift {!D} to carry widening tokens. + All operations delegate to inner domain, + except widening tokens are used to delay widenings. *) module Dom (D: Lattice.S) = struct include Lattice.Prod (D) (TS) @@ -86,6 +87,7 @@ struct (d', TS.join t1 t2) end +(** Lift {!S} to carry widening tokens with both local and global states. *) module Lifter (S: Spec): Spec = struct module D = @@ -121,23 +123,29 @@ struct let conv (ctx: (D.t, G.t, C.t, V.t) ctx): (S.D.t, S.G.t, S.C.t, S.V.t) ctx = { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es) + ; split = (fun d es -> ctx.split (d, snd ctx.local) es) (* Split keeps local widening tokens. *) ; global = (fun g -> G.unlift (ctx.global g)) - ; sideg = (fun v g -> ctx.sideg v (g, !side_tokens)) + ; sideg = (fun v g -> ctx.sideg v (g, !side_tokens)) (* Using side_tokens for side effect. *) } let lift_fun ctx f g h = - let ts = ref (snd ctx.local) in - let d = handle ~with_:(fun t -> ts := TS.add t !ts) (fun () -> - with_local_tokens (snd ctx.local) (fun () -> - h (g (conv ctx)) - ) - ) + let new_tokens = ref (snd ctx.local) in (* New tokens not yet used during this transfer function, such that it is deterministic. *) + let old_add = !add_ref in + let old_local_tokens = !local_tokens in + add_ref := (fun t -> new_tokens := TS.add t !new_tokens); + local_tokens := snd ctx.local; + let d = + Fun.protect (fun () -> + h (g (conv ctx)) + ) ~finally:(fun () -> + local_tokens := old_local_tokens; + add_ref := old_add + ) in (* If transfer function exits via exception, then new tokens are forgotten. There's nowhere to put them to potentially pass them to splits. Thus, this functor should not be used inside deadcode lifter. *) - f d !ts + f d !new_tokens let lift' d ts = (d, ts) @@ -161,4 +169,4 @@ struct let threadenter ctx lval f args = lift_fun ctx (fun l ts -> List.map (Fun.flip lift' ts) l) S.threadenter ((|>) args % (|>) f % (|>) lval) let threadspawn ctx lval f args fctx = lift_fun ctx lift' S.threadspawn ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) -end \ No newline at end of file +end From b5de6e63ed44b0f9f75ba9aaf6a100bfb2eb01b4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Oct 2022 17:32:17 +0300 Subject: [PATCH 089/132] Fix test ID conflict 56/19 --- ...me-strengthening.c => 25-apron-unassume-strengthening.c} | 2 +- ...trengthening.yml => 25-apron-unassume-strengthening.yml} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename tests/regression/56-witness/{19-apron-unassume-strengthening.c => 25-apron-unassume-strengthening.c} (84%) rename tests/regression/56-witness/{19-apron-unassume-strengthening.yml => 25-apron-unassume-strengthening.yml} (85%) diff --git a/tests/regression/56-witness/19-apron-unassume-strengthening.c b/tests/regression/56-witness/25-apron-unassume-strengthening.c similarity index 84% rename from tests/regression/56-witness/19-apron-unassume-strengthening.c rename to tests/regression/56-witness/25-apron-unassume-strengthening.c index c1de48dda0..12d125a845 100644 --- a/tests/regression/56-witness/19-apron-unassume-strengthening.c +++ b/tests/regression/56-witness/25-apron-unassume-strengthening.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 19-apron-unassume-strengthening.yml --enable ana.apron.strengthening --set sem.int.signed_overflow assume_none +// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 25-apron-unassume-strengthening.yml --enable ana.apron.strengthening --set sem.int.signed_overflow assume_none #include int main() { diff --git a/tests/regression/56-witness/19-apron-unassume-strengthening.yml b/tests/regression/56-witness/25-apron-unassume-strengthening.yml similarity index 85% rename from tests/regression/56-witness/19-apron-unassume-strengthening.yml rename to tests/regression/56-witness/25-apron-unassume-strengthening.yml index 44ef995387..86bdd3aa99 100644 --- a/tests/regression/56-witness/19-apron-unassume-strengthening.yml +++ b/tests/regression/56-witness/25-apron-unassume-strengthening.yml @@ -12,13 +12,13 @@ ''goblint-dir'' ''.goblint-56-19''' task: input_files: - - 19-apron-unassume-strengthening.c + - 25-apron-unassume-strengthening.c input_file_hashes: - 19-apron-unassume-strengthening.c: b2a50e3b423ade600ec2de58fa8ace6ec9a08b85fdc016cbdb7fbe9a3ba80d83 + 25-apron-unassume-strengthening.c: b2a50e3b423ade600ec2de58fa8ace6ec9a08b85fdc016cbdb7fbe9a3ba80d83 data_model: LP64 language: C location: - file_name: 19-apron-unassume-strengthening.c + file_name: 25-apron-unassume-strengthening.c file_hash: b2a50e3b423ade600ec2de58fa8ace6ec9a08b85fdc016cbdb7fbe9a3ba80d83 line: 8 column: 4 From de6c6813f52385f7f17ecc402866ce1a3952dc70 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Oct 2022 17:35:29 +0300 Subject: [PATCH 090/132] Add option ana.widen.tokens --- conf/bench-yaml-validate.json | 3 +++ conf/svcomp-yaml-validate.json | 3 +++ src/framework/control.ml | 2 +- src/util/options.schema.json | 13 +++++++++++++ src/util/wideningTokens.ml | 5 ++++- .../56-witness/12-apron-unassume-branch.c | 2 +- .../regression/56-witness/23-base-unassume-priv2.c | 2 +- .../regression/56-witness/24-apron-unassume-priv2.c | 2 +- 8 files changed, 27 insertions(+), 5 deletions(-) diff --git a/conf/bench-yaml-validate.json b/conf/bench-yaml-validate.json index 1d11742b48..504be1a02d 100644 --- a/conf/bench-yaml-validate.json +++ b/conf/bench-yaml-validate.json @@ -39,6 +39,9 @@ "ldv_xzalloc", "ldv_calloc" ] + }, + "widen": { + "tokens": true } }, "witness": { diff --git a/conf/svcomp-yaml-validate.json b/conf/svcomp-yaml-validate.json index 649168878f..f0a1e53fc4 100644 --- a/conf/svcomp-yaml-validate.json +++ b/conf/svcomp-yaml-validate.json @@ -54,6 +54,9 @@ "ldv_xzalloc", "ldv_calloc" ] + }, + "widen": { + "tokens": true } }, "exp": { diff --git a/src/framework/control.ml b/src/framework/control.ml index 9af48decc1..1e279fae86 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -31,7 +31,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( |> lift (get_bool "ana.opt.hashcons") (module HashconsLifter) (* Widening tokens must be outside of hashcons, because widening token domain ignores token sets for identity, so hashcons doesn't allow adding tokens. Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) - |> lift true (module WideningTokens.Lifter) + |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) ) in GobConfig.building_spec := false; Analyses.control_spec_c := (module S1.C); diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 25c524ca12..25e79ceb4f 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -923,6 +923,19 @@ } }, "additionalProperties": false + }, + "widen": { + "title": "ana.widen", + "type": "object", + "properties": { + "tokens": { + "title": "ana.widen.tokens", + "description": "Delay widenings using widening tokens.", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/src/util/wideningTokens.ml b/src/util/wideningTokens.ml index 721ccff660..917b184688 100644 --- a/src/util/wideningTokens.ml +++ b/src/util/wideningTokens.ml @@ -12,7 +12,10 @@ module Token = Basetype.RawStrings (* Change to variant type if need other token module TS = SetDomain.ToppedSet (Token) (struct let topname = "Top" end) (** Reference to current {!add} implementation. Maintained by {!Lifter}. *) -let add_ref: (Token.t -> unit) ref = ref (fun _ -> failwith "Unhandled token") +let add_ref: (Token.t -> unit) ref = ref (fun _ -> + if GobConfig.get_bool "ana.widen.tokens" then + failwith "Unhandled widening token" + ) (** Add widening token to local state. *) let add t = !add_ref t diff --git a/tests/regression/56-witness/12-apron-unassume-branch.c b/tests/regression/56-witness/12-apron-unassume-branch.c index c534c561f3..703b4406d9 100644 --- a/tests/regression/56-witness/12-apron-unassume-branch.c +++ b/tests/regression/56-witness/12-apron-unassume-branch.c @@ -1,6 +1,6 @@ // SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 12-apron-unassume-branch.yml #include - +// TODO: this was supposed to test local widening tokens, but thanks to apron narrowing works without int main() { int i = 0; while (i < 100) { diff --git a/tests/regression/56-witness/23-base-unassume-priv2.c b/tests/regression/56-witness/23-base-unassume-priv2.c index f495ae7978..17579872d9 100644 --- a/tests/regression/56-witness/23-base-unassume-priv2.c +++ b/tests/regression/56-witness/23-base-unassume-priv2.c @@ -1,4 +1,4 @@ -// PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 23-base-unassume-priv2.yml --set solvers.td3.side_widen always +// PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 23-base-unassume-priv2.yml --set solvers.td3.side_widen always --enable ana.widen.tokens #include #include diff --git a/tests/regression/56-witness/24-apron-unassume-priv2.c b/tests/regression/56-witness/24-apron-unassume-priv2.c index 2dc3d067a1..c1ccf2d461 100644 --- a/tests/regression/56-witness/24-apron-unassume-priv2.c +++ b/tests/regression/56-witness/24-apron-unassume-priv2.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.base.privatization none --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 24-apron-unassume-priv2.yml --set solvers.td3.side_widen always +// SKIP PARAM: --set ana.base.privatization none --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 24-apron-unassume-priv2.yml --set solvers.td3.side_widen always --enable ana.widen.tokens #include #include From 964bb7a6ffaaf3e3e7ff1ab7f3cd0c9e92da54bf Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Oct 2022 17:37:36 +0300 Subject: [PATCH 091/132] Fix 56-witness/12-apron-unassume-branch --- tests/regression/56-witness/12-apron-unassume-branch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/56-witness/12-apron-unassume-branch.c b/tests/regression/56-witness/12-apron-unassume-branch.c index 703b4406d9..2cd29c404b 100644 --- a/tests/regression/56-witness/12-apron-unassume-branch.c +++ b/tests/regression/56-witness/12-apron-unassume-branch.c @@ -1,6 +1,6 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 12-apron-unassume-branch.yml +// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 12-apron-unassume-branch.yml --set ana.apron.domain polyhedra --enable ana.widen.tokens #include -// TODO: this was supposed to test local widening tokens, but thanks to apron narrowing works without +// Uses polyhedra instead of octagon such that widening tokens are actually needed by test instead of narrowing. int main() { int i = 0; while (i < 100) { From 1ea599742bdf877007138804b35e8c92e830a781 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Oct 2022 17:46:31 +0300 Subject: [PATCH 092/132] Support witness.invariant.exact in apron --- src/analyses/apron/apronAnalysis.apron.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index 97a2ff1207..76e563c84e 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -502,6 +502,7 @@ struct let keep_local = GobConfig.get_bool "ana.apron.invariant.local" in let keep_global = GobConfig.get_bool "ana.apron.invariant.global" in let one_var = GobConfig.get_bool "ana.apron.invariant.one-var" in + let exact = GobConfig.get_bool "witness.invariant.exact" in let ask = Analyses.ask_of_ctx ctx in let scope = Node.find_fundec ctx.node in @@ -541,8 +542,9 @@ struct AD.invariant ~scope apr |> List.enum |> Enum.filter_map (fun (lincons1: Lincons1.t) -> - (* filter one-vars *) - if one_var || Apron.Linexpr0.get_size lincons1.lincons0.linexpr0 >= 2 then + (* filter one-vars and exact *) + (* TODO: exact filtering doesn't really work with octagon because it returns two SUPEQ constraints instead *) + if (one_var || Apron.Linexpr0.get_size lincons1.lincons0.linexpr0 >= 2) && (exact || Lincons1.get_typ lincons1 <> EQ) then CilOfApron.cil_exp_of_lincons1 lincons1 |> Option.map e_inv |> Option.filter (fun exp -> not (InvariantCil.exp_contains_tmp exp) && InvariantCil.exp_is_in_scope scope exp) @@ -571,9 +573,7 @@ struct | Queries.IterSysVars (vq, vf) -> let vf' x = vf (Obj.repr x) in Priv.iter_sys_vars ctx.global vq vf' - | Queries.Invariant context -> - (* TODO: witness.invariant.exact *) - query_invariant ctx context + | Queries.Invariant context -> query_invariant ctx context | _ -> Result.top q From 14d832084d8e315b7acd0e0273dfd3abbf9527ad Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 10:50:29 +0300 Subject: [PATCH 093/132] Add option ana.base.invariant.unassume --- src/analyses/base.ml | 12 ++++++++++-- src/util/options.schema.json | 7 +++++++ tests/regression/56-witness/17-base-unassume-tauto.c | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 825a6767bd..ac8d865a54 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2494,9 +2494,17 @@ struct with Deadcode -> (* contradiction in unassume *) D.bot () in - let module DFP = LocalFixpoint.Make (D) in if M.tracing then M.traceli "unassume" "base unassuming\n"; - let r = DFP.lfp f in + let r = + match get_string "ana.base.invariant.unassume" with + | "once" -> + f (D.bot ()) + | "fixpoint" -> + let module DFP = LocalFixpoint.Make (D) in + DFP.lfp f + | _ -> + assert false + in if M.tracing then M.traceu "unassume" "base unassumed\n"; r in diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 25e79ceb4f..140d129e20 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -693,6 +693,13 @@ "description": "Generate base analysis invariants", "type": "boolean", "default": true + }, + "unassume": { + "title": "ana.base.invariant.unassume", + "description": "How many times to unassume an invariant: once or until fixpoint (at least twice as expensive).", + "type": "string", + "enum": ["once", "fixpoint"], + "default": "once" } }, "additionalProperties": false diff --git a/tests/regression/56-witness/17-base-unassume-tauto.c b/tests/regression/56-witness/17-base-unassume-tauto.c index 5b7be9828b..eb5ac8a262 100644 --- a/tests/regression/56-witness/17-base-unassume-tauto.c +++ b/tests/regression/56-witness/17-base-unassume-tauto.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 17-base-unassume-tauto.yml --enable ana.int.interval +// PARAM: --set ana.activated[+] unassume --set witness.yaml.unassume 17-base-unassume-tauto.yml --enable ana.int.interval --set ana.base.invariant.unassume fixpoint #include int main() { From 6f31a881aebdf64a43027f44f5b985d62b3c3469 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 12:47:33 +0300 Subject: [PATCH 094/132] Add option witness.invariant.inexact-type-bounds --- src/cdomains/intDomain.ml | 13 +++++++------ src/util/options.schema.json | 6 ++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index b94daa9ce2..ec5134bef8 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -840,10 +840,9 @@ struct else top_bool let invariant_ikind e ik x = - let exact = get_bool "witness.invariant.exact" in match x with | Some (x1, x2) when Ints_t.compare x1 x2 = 0 -> - if exact then + if get_bool "witness.invariant.exact" then let x1 = Ints_t.to_bigint x1 in Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x1, intType)) else @@ -851,8 +850,9 @@ struct | Some (x1, x2) -> let (min_ik, max_ik) = range ik in let (x1', x2') = BatTuple.Tuple2.mapn (Ints_t.to_bigint) (x1, x2) in - let i1 = if not exact || Ints_t.compare min_ik x1 <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1', e, intType)) else Invariant.none in - let i2 = if not exact || Ints_t.compare x2 max_ik <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2', intType)) else Invariant.none in + let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in + let i1 = if inexact_type_bounds || Ints_t.compare min_ik x1 <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1', e, intType)) else Invariant.none in + let i2 = if inexact_type_bounds || Ints_t.compare x2 max_ik <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2', intType)) else Invariant.none in Invariant.(i1 && i2) | None -> Invariant.none @@ -1631,8 +1631,9 @@ struct let ikr = size ik in (Exclusion.min_of_range ikr, Exclusion.max_of_range ikr) in - let imin = if (* not exact || *) BI.compare ikmin rmin <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik rmin, e, intType)) else Invariant.none in - let imax = if (* not exact || *) BI.compare rmax ikmax <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik rmax, intType)) else Invariant.none in + let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in + let imin = if inexact_type_bounds || BI.compare ikmin rmin <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik rmin, e, intType)) else Invariant.none in + let imax = if inexact_type_bounds || BI.compare rmax ikmax <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik rmax, intType)) else Invariant.none in S.fold (fun x a -> let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in Invariant.(a && i) diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 140d129e20..00c3afbf5d 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -2066,6 +2066,12 @@ "type": "boolean", "default": true }, + "inexact-type-bounds": { + "title": "witness.invariant.inexact-type-bounds", + "description": "Emit inexact invariants matching type bounds.", + "type": "boolean", + "default": false + }, "exclude-vars": { "title": "witness.invariant.exclude-vars", "description": "OCaml Str regexes for variables to exclude from invariants.", From e4a316aa43e58a24d30ceeafa45044ad113fb6ce Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 13:40:26 +0300 Subject: [PATCH 095/132] Add option witness.yaml.entry-types --- src/analyses/unassumeAnalysis.ml | 13 +- src/util/options.schema.json | 21 ++ src/witness/yamlWitness.ml | 317 +++++++++++++++++-------------- 3 files changed, 207 insertions(+), 144 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index ccbe2773a1..db7896b5d2 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -86,6 +86,7 @@ struct let unassume_entry (entry: YamlWitnessType.Entry.t) = let uuid = entry.metadata.uuid in + let target_type = YamlWitnessType.EntryType.entry_type entry.entry_type in let unassume_nodes_invariant ~loc ~nodes inv = let msgLoc: M.Location.t = CilLocation loc in @@ -172,13 +173,15 @@ struct M.warn ~category:Witness ~loc:msgLoc "couldn't locate invariant: %s" inv in - match entry.entry_type with - | LoopInvariant x -> + match YamlWitness.entry_type_enabled target_type, entry.entry_type with + | true, LoopInvariant x -> unassume_loop_invariant x - | PreconditionLoopInvariant x -> + | true, PreconditionLoopInvariant x -> unassume_precondition_loop_invariant x - | entry_type -> - M.info_noloc ~category:Witness "cannot unassume entry of type %s" (YamlWitnessType.EntryType.entry_type entry_type) + | false, (LoopInvariant _ | PreconditionLoopInvariant _) -> + M.info_noloc ~category:Witness "disabled entry of type %s" target_type + | _ -> + M.info_noloc ~category:Witness "cannot unassume entry of type %s" target_type in List.iter (fun yaml_entry -> diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 00c3afbf5d..4d4124f57a 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -2123,6 +2123,27 @@ "type": "boolean", "default": false }, + "entry-types": { + "title": "witness.yaml.entry-types", + "description": "YAML witness entry types to output/input.", + "type": "array", + "items": { + "type": "string", + "enum": [ + "loop_invariant", + "flow_insensitive_invariant", + "precondition_loop_invariant", + "loop_invariant_certificate", + "precondition_loop_invariant_certificate" + ] + }, + "default": [ + "loop_invariant", + "flow_insensitive_invariant", + "loop_invariant_certificate", + "precondition_loop_invariant_certificate" + ] + }, "path": { "title": "witness.yaml.path", "description": "YAML witness output path", diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 54ff5b8c8b..97676084a3 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -118,6 +118,9 @@ let yaml_entries_to_file yaml_entries file = let text = Yaml.to_string_exn ~len:(List.length yaml_entries * 4096 + 2048) yaml in Batteries.output_file ~filename:(Fpath.to_string file) ~text +let entry_type_enabled entry_type = + List.mem entry_type (GobConfig.get_string_list "witness.yaml.entry-types") + module Query (Spec : Spec) (EQSys : GlobConstrSys with module LVar = VarF (Spec.C) @@ -227,7 +230,7 @@ struct in let task = Entry.task ~input_files ~data_model ~specification in - let nh = join_contexts lh in + let nh = join_contexts lh in (* TODO: make lazy *) let is_invariant_node (n : Node.t) = let loc = Node.location n in @@ -275,47 +278,59 @@ struct CilLval.Set.top () in - (* Generate location invariants (wihtout precondition) *) - let entries = NH.fold (fun n local acc -> - let loc = Node.location n in - if is_invariant_node n then ( - let lvals = local_lvals n local in - match Query.ask_local_node gh n local (Invariant {Invariant.default_context with lvals}) with - | `Lifted inv -> - let invs = WitnessUtil.InvariantExp.process_exp inv in - List.fold_left (fun acc inv -> - let location_function = (Node.find_fundec n).svar.vname in - let location = Entry.location ~location:loc ~location_function in - let invariant = Entry.invariant (CilType.Exp.show inv) in - let entry = Entry.loop_invariant ~task ~location ~invariant in - entry :: acc - ) acc invs - | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) - acc - ) - else - acc - ) nh [] + let entries = [] in + + (* Generate location invariants (without precondition) *) + let entries = + if entry_type_enabled YamlWitnessType.LoopInvariant.entry_type then ( + NH.fold (fun n local acc -> + let loc = Node.location n in + if is_invariant_node n then ( + let lvals = local_lvals n local in + match Query.ask_local_node gh n local (Invariant {Invariant.default_context with lvals}) with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let location_function = (Node.find_fundec n).svar.vname in + let location = Entry.location ~location:loc ~location_function in + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.loop_invariant ~task ~location ~invariant in + entry :: acc + ) acc invs + | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) + acc + ) + else + acc + ) nh entries + ) + else + entries in (* Generate flow-insensitive invariants *) - let entries = GHT.fold (fun g v acc -> - match g with - | `Left g -> (* Spec global *) - begin match Query.ask_global gh (InvariantGlobal (Obj.repr g)) with - | `Lifted inv -> - let invs = WitnessUtil.InvariantExp.process_exp inv in - List.fold_left (fun acc inv -> - let invariant = Entry.invariant (CilType.Exp.show inv) in - let entry = Entry.flow_insensitive_invariant ~task ~invariant in - entry :: acc - ) acc invs - | `Bot | `Top -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) + let entries = + if entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type then ( + GHT.fold (fun g v acc -> + match g with + | `Left g -> (* Spec global *) + begin match Query.ask_global gh (InvariantGlobal (Obj.repr g)) with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + List.fold_left (fun acc inv -> + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.flow_insensitive_invariant ~task ~invariant in + entry :: acc + ) acc invs + | `Bot | `Top -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) + acc + end + | `Right _ -> (* contexts global *) acc - end - | `Right _ -> (* contexts global *) - acc - ) gh entries + ) gh entries + ) + else + entries in (* Generate precondition invariants. @@ -323,96 +338,100 @@ struct 1. Collect contexts for each function 2. For each function context, find "matching"/"weaker" contexts that may satisfy its invariant 3. Generate precondition invariants. The postcondition is a disjunction over the invariants for matching states. *) - - (* 1. Collect contexts for each function *) - (* TODO: Use [IterSysVars] for this when #391 is merged. *) - let fun_contexts : con_inv list FMap.t = FMap.create 103 in - LHT.iter (fun ((n, c) as lvar) local -> - begin match n with - | FunctionEntry f -> - let invariant = Query.ask_local gh lvar local (Invariant Invariant.default_context) in - FMap.modify_def [] f (fun acc -> {context = c; invariant; node = n; state = local}::acc) fun_contexts - | _ -> () - end - ) lh; - - (* 2. For all contexts and their invariants, find all contexts such that their start state may satisfy the invariant. *) - let fc_map : con_inv list FCMap.t = FCMap.create 103 in - FMap.iter (fun f con_invs -> - List.iter (fun current_c -> - begin match current_c.invariant with + let entries = + if entry_type_enabled YamlWitnessType.PreconditionLoopInvariant.entry_type then ( + (* 1. Collect contexts for each function *) + (* TODO: Use [IterSysVars] for this when #391 is merged. *) + let fun_contexts : con_inv list FMap.t = FMap.create 103 in + LHT.iter (fun ((n, c) as lvar) local -> + begin match n with + | FunctionEntry f -> + let invariant = Query.ask_local gh lvar local (Invariant Invariant.default_context) in + FMap.modify_def [] f (fun acc -> {context = c; invariant; node = n; state = local}::acc) fun_contexts + | _ -> () + end + ) lh; + + (* 2. For all contexts and their invariants, find all contexts such that their start state may satisfy the invariant. *) + let fc_map : con_inv list FCMap.t = FCMap.create 103 in + FMap.iter (fun f con_invs -> + List.iter (fun current_c -> + begin match current_c.invariant with + | `Lifted c_inv -> + (* Collect all start states that may satisfy the invariant of current_c *) + List.iter (fun c -> + let x = Query.ask_local gh (c.node, c.context) c.state (Queries.EvalInt c_inv) in + if Queries.ID.is_bot x || Queries.ID.is_bot_ikind x then (* dead code *) + failwith "Bottom not expected when querying context state" (* Maybe this is reachable, failwith for now so we see when this happens *) + else if Queries.ID.to_bool x = Some false then () (* Nothing to do, the c does definitely not satisfy the predicate of current_c *) + else begin + (* Insert c into the list of weaker contexts of f *) + FCMap.modify_def [] (f, current_c.context) (fun cs -> c::cs) fc_map; + end + ) con_invs; + | `Bot | `Top -> + (* If the context invariant is None, we will not generate a precondition invariant. Nothing to do here. *) + () + end + ) con_invs; + ) fun_contexts; + + (** Given [(n,c)] retrieves all [(n,c')], with [c'] such that [(f, c')] may satisfy the precondition generated for [c].*) + let find_matching_states ((n, c) : LHT.key) = + let f = Node.find_fundec n in + let contexts = FCMap.find fc_map (f, c) in + List.filter_map (fun c -> LHT.find_option lh (n, c.context)) contexts + in + + (* 3. Generate precondition invariants *) + LHT.fold (fun ((n, c) as lvar) local acc -> + if is_invariant_node n then ( + let fundec = Node.find_fundec n in + let pre_lvar = (Node.FunctionEntry fundec, c) in + let pre_local = LHT.find lh pre_lvar in + let query = Queries.Invariant Invariant.default_context in + match Query.ask_local gh pre_lvar pre_local query with | `Lifted c_inv -> - (* Collect all start states that may satisfy the invariant of current_c *) - List.iter (fun c -> - let x = Query.ask_local gh (c.node, c.context) c.state (Queries.EvalInt c_inv) in - if Queries.ID.is_bot x || Queries.ID.is_bot_ikind x then (* dead code *) - failwith "Bottom not expected when querying context state" (* Maybe this is reachable, failwith for now so we see when this happens *) - else if Queries.ID.to_bool x = Some false then () (* Nothing to do, the c does definitely not satisfy the predicate of current_c *) - else begin - (* Insert c into the list of weaker contexts of f *) - FCMap.modify_def [] (f, current_c.context) (fun cs -> c::cs) fc_map; + let loc = Node.location n in + (* Find unknowns for which the preceding start state satisfies the precondtion *) + let xs = find_matching_states lvar in + + (* Generate invariants. Give up in case one invariant could not be generated. *) + let invs = GobList.fold_while_some (fun acc local -> + let lvals = local_lvals n local in + match Query.ask_local_node gh n local (Invariant {Invariant.default_context with lvals}) with + | `Lifted c -> Some ((`Lifted c)::acc) + | `Bot | `Top -> None + ) [] xs + in + begin match invs with + | None + | Some [] -> acc + | Some (x::xs) -> + begin match List.fold_left (fun acc inv -> Invariant.(acc || inv)) x xs with + | `Lifted inv -> + let invs = WitnessUtil.InvariantExp.process_exp inv in + let c_inv = InvariantCil.exp_replace_original_name c_inv in (* cannot be split *) + List.fold_left (fun acc inv -> + let location_function = (Node.find_fundec n).svar.vname in + let location = Entry.location ~location:loc ~location_function in + let precondition = Entry.invariant (CilType.Exp.show c_inv) in + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.precondition_loop_invariant ~task ~location ~precondition ~invariant in + entry :: acc + ) acc invs + | `Bot | `Top -> acc end - ) con_invs; - | `Bot | `Top -> - (* If the context invariant is None, we will not generate a precondition invariant. Nothing to do here. *) - () - end - ) con_invs; - ) fun_contexts; - - (** Given [(n,c)] retrieves all [(n,c')], with [c'] such that [(f, c')] may satisfy the precondition generated for [c].*) - let find_matching_states ((n, c) : LHT.key) = - let f = Node.find_fundec n in - let contexts = FCMap.find fc_map (f, c) in - List.filter_map (fun c -> LHT.find_option lh (n, c.context)) contexts - in - - (* 3. Generate precondition invariants *) - let entries = LHT.fold (fun ((n, c) as lvar) local acc -> - if is_invariant_node n then ( - let fundec = Node.find_fundec n in - let pre_lvar = (Node.FunctionEntry fundec, c) in - let pre_local = LHT.find lh pre_lvar in - let query = Queries.Invariant Invariant.default_context in - match Query.ask_local gh pre_lvar pre_local query with - | `Lifted c_inv -> - let loc = Node.location n in - (* Find unknowns for which the preceding start state satisfies the precondtion *) - let xs = find_matching_states lvar in - - (* Generate invariants. Give up in case one invariant could not be generated. *) - let invs = GobList.fold_while_some (fun acc local -> - let lvals = local_lvals n local in - match Query.ask_local_node gh n local (Invariant {Invariant.default_context with lvals}) with - | `Lifted c -> Some ((`Lifted c)::acc) - | `Bot | `Top -> None - ) [] xs - in - begin match invs with - | None - | Some [] -> acc - | Some (x::xs) -> - begin match List.fold_left (fun acc inv -> Invariant.(acc || inv)) x xs with - | `Lifted inv -> - let invs = WitnessUtil.InvariantExp.process_exp inv in - let c_inv = InvariantCil.exp_replace_original_name c_inv in (* cannot be split *) - List.fold_left (fun acc inv -> - let location_function = (Node.find_fundec n).svar.vname in - let location = Entry.location ~location:loc ~location_function in - let precondition = Entry.invariant (CilType.Exp.show c_inv) in - let invariant = Entry.invariant (CilType.Exp.show inv) in - let entry = Entry.precondition_loop_invariant ~task ~location ~precondition ~invariant in - entry :: acc - ) acc invs - | `Bot | `Top -> acc end - end - | _ -> (* Do not construct precondition invariants if we cannot express precondition *) - acc - ) - else - acc - ) lh entries + | _ -> (* Do not construct precondition invariants if we cannot express precondition *) + acc + ) + else + acc + ) lh entries + ) + else + entries in let yaml_entries = List.rev_map YamlWitnessType.Entry.to_yaml entries in (* reverse to make entries in file in the same order as generation messages *) @@ -490,6 +509,7 @@ struct let cnt_unchecked = ref 0 in let cnt_unsupported = ref 0 in let cnt_error = ref 0 in + let cnt_disabled = ref 0 in let validate_entry (entry: YamlWitnessType.Entry.t): YamlWitnessType.Entry.t option = let uuid = entry.metadata.uuid in @@ -526,20 +546,24 @@ struct | Confirmed -> incr cnt_confirmed; M.success ~category:Witness ~loc:msgLoc "invariant confirmed: %s" inv; - let target = Entry.target ~uuid ~type_:target_type ~file_name:loc.file in - let certification = Entry.certification true in - let certificate_entry = entry_certificate ~target ~certification in - Some certificate_entry + Option.map (fun entry_certificate -> + let target = Entry.target ~uuid ~type_:target_type ~file_name:loc.file in + let certification = Entry.certification true in + let certificate_entry = entry_certificate ~target ~certification in + certificate_entry + ) entry_certificate | Unconfirmed -> incr cnt_unconfirmed; M.warn ~category:Witness ~loc:msgLoc "invariant unconfirmed: %s" inv;None | Refuted -> incr cnt_refuted; M.error ~category:Witness ~loc:msgLoc "invariant refuted: %s" inv; - let target = Entry.target ~uuid ~type_:target_type ~file_name:loc.file in - let certification = Entry.certification false in - let certificate_entry = entry_certificate ~target ~certification in - Some certificate_entry + Option.map (fun entry_certificate -> + let target = Entry.target ~uuid ~type_:target_type ~file_name:loc.file in + let certification = Entry.certification false in + let certificate_entry = entry_certificate ~target ~certification in + certificate_entry + ) entry_certificate | ParseError -> incr cnt_error; M.error ~category:Witness ~loc:msgLoc "CIL couldn't parse invariant: %s" inv; @@ -556,7 +580,12 @@ struct let validate_loop_invariant (loop_invariant: YamlWitnessType.LoopInvariant.t) = let loc = loc_of_location loop_invariant.location in let inv = loop_invariant.loop_invariant.string in - let entry_certificate = Entry.loop_invariant_certificate in + let entry_certificate = + if entry_type_enabled YamlWitnessType.LoopInvariantCertificate.entry_type then + Some Entry.loop_invariant_certificate + else + None + in let msgLoc: M.Location.t = CilLocation loc in match Locator.find_opt locator loc with @@ -572,7 +601,12 @@ struct let loc = loc_of_location precondition_loop_invariant.location in let pre = precondition_loop_invariant.precondition.string in let inv = precondition_loop_invariant.loop_invariant.string in - let entry_certificate = Entry.precondition_loop_invariant_certificate in + let entry_certificate = + if entry_type_enabled YamlWitnessType.PreconditionLoopInvariantCertificate.entry_type then + Some Entry.precondition_loop_invariant_certificate + else + None + in let msgLoc: M.Location.t = CilLocation loc in match Locator.find_opt locator loc with @@ -620,11 +654,15 @@ struct None in - match entry.entry_type with - | LoopInvariant x -> + match entry_type_enabled target_type, entry.entry_type with + | true, LoopInvariant x -> validate_loop_invariant x - | PreconditionLoopInvariant x -> + | true, PreconditionLoopInvariant x -> validate_precondition_loop_invariant x + | false, (LoopInvariant _ | PreconditionLoopInvariant _) -> + incr cnt_disabled; + M.info_noloc ~category:Witness "disabled entry of type %s" target_type; + None | _ -> incr cnt_unsupported; M.info_noloc ~category:Witness "cannot validate entry of type %s" target_type; @@ -651,7 +689,8 @@ struct (Pretty.dprintf "error: %d" !cnt_error, None); (Pretty.dprintf "unchecked: %d" !cnt_unchecked, None); (Pretty.dprintf "unsupported: %d" !cnt_unsupported, None); - (Pretty.dprintf "total: %d" (!cnt_confirmed + !cnt_unconfirmed + !cnt_refuted + !cnt_unchecked + !cnt_unsupported + !cnt_error), None); + (Pretty.dprintf "disabled: %d" !cnt_disabled, None); + (Pretty.dprintf "total: %d" (!cnt_confirmed + !cnt_unconfirmed + !cnt_refuted + !cnt_unchecked + !cnt_unsupported + !cnt_error + !cnt_disabled), None); ]; yaml_entries_to_file (List.rev yaml_entries') (Fpath.v (GobConfig.get_string "witness.yaml.certificate")) From ce82c4168526c105674f6b473179ce1fa2324f42 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 13:42:34 +0300 Subject: [PATCH 096/132] Make YAML witness generation context joining lazy Depending on options, it might be unnecessary. --- src/witness/yamlWitness.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 97676084a3..c9eb4741c2 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -230,7 +230,7 @@ struct in let task = Entry.task ~input_files ~data_model ~specification in - let nh = join_contexts lh in (* TODO: make lazy *) + let nh = lazy (join_contexts lh) in let is_invariant_node (n : Node.t) = let loc = Node.location n in @@ -259,7 +259,7 @@ struct Cfg.next n |> BatList.enum |> BatEnum.filter_map (fun (_, next_n) -> - let next_local = try NH.find nh next_n with Not_found -> Spec.D.bot () in + let next_local = try NH.find (Lazy.force nh) next_n with Not_found -> Spec.D.bot () in match Query.ask_local_node gh next_n next_local MayAccessed with | `Top -> None | `Lifted _ as es -> Some es) @@ -302,7 +302,7 @@ struct ) else acc - ) nh entries + ) (Lazy.force nh) entries ) else entries From 1e981de3d79f402322fed1ed6e544097a9ede2d5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 7 Oct 2022 12:03:26 +0300 Subject: [PATCH 097/132] Remove temporary YAML BenchExec def --- sv-comp/my-bench-sv-comp/.gitignore | 1 + .../yaml/goblint-validate-tmp.xml | 26 ------------------- 2 files changed, 1 insertion(+), 26 deletions(-) create mode 100644 sv-comp/my-bench-sv-comp/.gitignore delete mode 100644 sv-comp/my-bench-sv-comp/yaml/goblint-validate-tmp.xml diff --git a/sv-comp/my-bench-sv-comp/.gitignore b/sv-comp/my-bench-sv-comp/.gitignore new file mode 100644 index 0000000000..2eb047c8d6 --- /dev/null +++ b/sv-comp/my-bench-sv-comp/.gitignore @@ -0,0 +1 @@ +*-tmp.xml diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint-validate-tmp.xml b/sv-comp/my-bench-sv-comp/yaml/goblint-validate-tmp.xml deleted file mode 100644 index fe912571b0..0000000000 --- a/sv-comp/my-bench-sv-comp/yaml/goblint-validate-tmp.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - evals = (\d+) - - - - - /mnt/goblint-svcomp/benchexec/results/yaml-5-evals/goblint.2022-09-15_14-37-40.files/${rundefinition_name}/${taskdef_name}/witness.yml - - - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/ReachSafety-Loops-Simple.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp - - - - - From 9426848d93ef8056691fd0f7574958c2d7f629f3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 7 Oct 2022 12:10:17 +0300 Subject: [PATCH 098/132] Fix 56-witness/10-apron-unassume-interval to not succeed because of octagon narrowing --- tests/regression/56-witness/10-apron-unassume-interval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/56-witness/10-apron-unassume-interval.c b/tests/regression/56-witness/10-apron-unassume-interval.c index 895f6e8533..e35d42d563 100644 --- a/tests/regression/56-witness/10-apron-unassume-interval.c +++ b/tests/regression/56-witness/10-apron-unassume-interval.c @@ -1,6 +1,6 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 10-apron-unassume-interval.yml +// SKIP PARAM: --set ana.activated[+] apron --set ana.apron.domain polyhedra --set ana.activated[+] unassume --set witness.yaml.unassume 10-apron-unassume-interval.yml #include - +// Using polyhedra instead of octagon, because the former has no narrowing and really needs the witness. int main() { int i = 0; while (i < 100) { From fa651e35ae5f654b14be68a747d593ce04127df6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 7 Oct 2022 12:55:00 +0300 Subject: [PATCH 099/132] Fix base invariant not excluding interval bounds --- src/analyses/baseInvariant.ml | 21 ++++++++++++++++-- .../17-interval-exclude-bound.c | 22 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 tests/regression/27-inv_invariants/17-interval-exclude-bound.c diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 203d0ab865..e9d53eac53 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -266,11 +266,28 @@ struct (* Both values can not be in the meet together, but it's not sound to exclude the meet from both. * e.g. a=[0,1], b=[1,2], meet a b = [1,1], but (a != b) does not imply a=[0,0], b=[2,2] since others are possible: a=[1,1], b=[2,2] * Only if a is a definite value, we can exclude it from b: *) - (* TODO: This causes inconsistent results: + (* Used to cause inconsistent results: interval not sufficiently refined: inv_bin_int: unequal: (Unknown int([-31,31]),[0,1]) and (0,[0,0]); ikind: int; a': (Not {0}([-31,31]),[-2147483648,2147483647]), b': (0,[0,0]) binop: m == 0, a': (Not {0}([-31,31]),[0,1]), b': (0,[0,0]) *) - let excl a b = match ID.to_int a with Some x -> ID.of_excl_list ikind [x] | None -> b in + let excl a b = + match ID.to_int a with + | Some x -> + let ex1 = ID.of_excl_list ikind [x] in + let ex2 = + (* Fix previously inconsistent results by excluding interval bounds. *) + let top_ik = ID.top_of ikind in + match ID.minimal b, ID.maximal b with + | Some lb, Some ub -> + let starting = if Z.equal lb x then ID.starting ikind (Z.add lb Z.one) else top_ik in + let ending = if Z.equal ub x then ID.ending ikind (Z.sub ub Z.one) else top_ik in + ID.meet starting ending + | _ -> + top_ik + in + ID.meet ex1 ex2 + | None -> b + in let a' = excl b a in let b' = excl a b in if M.tracing then M.tracel "inv" "inv_bin_int: unequal: %a and %a; ikind: %a; a': %a, b': %a\n" ID.pretty a ID.pretty b d_ikind ikind ID.pretty a' ID.pretty b'; diff --git a/tests/regression/27-inv_invariants/17-interval-exclude-bound.c b/tests/regression/27-inv_invariants/17-interval-exclude-bound.c new file mode 100644 index 0000000000..2115125be9 --- /dev/null +++ b/tests/regression/27-inv_invariants/17-interval-exclude-bound.c @@ -0,0 +1,22 @@ +// PARAM: --enable ana.int.interval + +int main() { + unsigned int r; // non-neg rand + int x = r % 100; + + // exclude lower bound + if (x != 0) + __goblint_check(x >= 1); + + if (x == 0) {} + else + __goblint_check(x >= 1); + + // exclude upper bound + if (x != 99) + __goblint_check(x <= 98); + + if (x == 99) {} + else + __goblint_check(x <= 98); +} From 73124ac03cd3ba7a83ef8bfa3341e850cde91225 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 7 Oct 2022 14:45:44 +0300 Subject: [PATCH 100/132] =?UTF-8?q?Add=20Min=C3=A9=20tutorial=20examples?= =?UTF-8?q?=20for=20unassume?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../56-witness/26-mine-tutorial-ex4.6.c | 12 ++++++++ .../56-witness/26-mine-tutorial-ex4.6.yml | 27 +++++++++++++++++ .../56-witness/27-mine-tutorial-ex4.7.c | 16 ++++++++++ .../56-witness/27-mine-tutorial-ex4.7.yml | 29 ++++++++++++++++++ .../56-witness/28-mine-tutorial-ex4.8.c | 14 +++++++++ .../56-witness/28-mine-tutorial-ex4.8.yml | 29 ++++++++++++++++++ .../56-witness/29-mine-tutorial-ex4.10.c | 14 +++++++++ .../56-witness/29-mine-tutorial-ex4.10.yml | 30 +++++++++++++++++++ 8 files changed, 171 insertions(+) create mode 100644 tests/regression/56-witness/26-mine-tutorial-ex4.6.c create mode 100644 tests/regression/56-witness/26-mine-tutorial-ex4.6.yml create mode 100644 tests/regression/56-witness/27-mine-tutorial-ex4.7.c create mode 100644 tests/regression/56-witness/27-mine-tutorial-ex4.7.yml create mode 100644 tests/regression/56-witness/28-mine-tutorial-ex4.8.c create mode 100644 tests/regression/56-witness/28-mine-tutorial-ex4.8.yml create mode 100644 tests/regression/56-witness/29-mine-tutorial-ex4.10.c create mode 100644 tests/regression/56-witness/29-mine-tutorial-ex4.10.yml diff --git a/tests/regression/56-witness/26-mine-tutorial-ex4.6.c b/tests/regression/56-witness/26-mine-tutorial-ex4.6.c new file mode 100644 index 0000000000..311f1a6d95 --- /dev/null +++ b/tests/regression/56-witness/26-mine-tutorial-ex4.6.c @@ -0,0 +1,12 @@ +// PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 26-mine-tutorial-ex4.6.yml + +int main() { + int x = 40; + while (x != 0) { + __goblint_check(x <= 40); + x--; + __goblint_check(x >= 0); + } + __goblint_check(x == 0); // no witness needed, just by invariant + return 0; +} diff --git a/tests/regression/56-witness/26-mine-tutorial-ex4.6.yml b/tests/regression/56-witness/26-mine-tutorial-ex4.6.yml new file mode 100644 index 0000000000..659537ba77 --- /dev/null +++ b/tests/regression/56-witness/26-mine-tutorial-ex4.6.yml @@ -0,0 +1,27 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 0e84a9de-b9f6-44dd-ab8d-ebdeca941482 + creation_time: 2022-10-07T09:14:31Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g1e981de3d-dirty + command_line: '''../../../goblint'' ''--enable'' ''ana.int.interval'' ''26-mine-tutorial-ex4.6.c'' + ''--html'' ''--set'' ''exp.g2html_path'' ''../../..'' ''--enable'' ''witness.yaml.enabled''' + task: + input_files: + - 26-mine-tutorial-ex4.6.c + input_file_hashes: + 26-mine-tutorial-ex4.6.c: e119cc77f5ce2a60d7c2b65c5dfbe8037f877b5621deab62b9580c5bbdebd97b + data_model: LP64 + language: C + location: + file_name: 26-mine-tutorial-ex4.6.c + file_hash: e119cc77f5ce2a60d7c2b65c5dfbe8037f877b5621deab62b9580c5bbdebd97b + line: 5 + column: 2 + function: main + loop_invariant: + string: 0 <= x && x <= 40 + type: assertion + format: C diff --git a/tests/regression/56-witness/27-mine-tutorial-ex4.7.c b/tests/regression/56-witness/27-mine-tutorial-ex4.7.c new file mode 100644 index 0000000000..6731df583c --- /dev/null +++ b/tests/regression/56-witness/27-mine-tutorial-ex4.7.c @@ -0,0 +1,16 @@ +// PARAM: --enable ana.int.interval --enable ana.sv-comp.functions --set ana.activated[+] unassume --set witness.yaml.unassume 27-mine-tutorial-ex4.7.yml +extern _Bool __VERIFIER_nondet_bool(); + +int main() { + int x = 0; + while (__VERIFIER_nondet_bool() == 0) { + __goblint_check(0 <= x); + __goblint_check(x <= 40); + if (__VERIFIER_nondet_bool() == 0) { + x++; + if (x > 40) + x = 0; + } + } + return 0; +} diff --git a/tests/regression/56-witness/27-mine-tutorial-ex4.7.yml b/tests/regression/56-witness/27-mine-tutorial-ex4.7.yml new file mode 100644 index 0000000000..8c94ac8e20 --- /dev/null +++ b/tests/regression/56-witness/27-mine-tutorial-ex4.7.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: e17f9860-95af-4213-a767-ab4876ead27e + creation_time: 2022-10-07T10:33:01Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''27-mine-tutorial-ex4.7.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''ana.sv-comp.functions'' ''--enable'' + ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' + ''--set'' ''goblint-dir'' ''.goblint-56-27''' + task: + input_files: + - 27-mine-tutorial-ex4.7.c + input_file_hashes: + 27-mine-tutorial-ex4.7.c: 4d06e38718d405171bd503e630f6c7a247bb3b0d3c1c6c951466e4989883b43c + data_model: LP64 + language: C + location: + file_name: 27-mine-tutorial-ex4.7.c + file_hash: 4d06e38718d405171bd503e630f6c7a247bb3b0d3c1c6c951466e4989883b43c + line: 6 + column: 8 + function: main + loop_invariant: + string: 0 <= x && x <= 40 + type: assertion + format: C diff --git a/tests/regression/56-witness/28-mine-tutorial-ex4.8.c b/tests/regression/56-witness/28-mine-tutorial-ex4.8.c new file mode 100644 index 0000000000..b7a8b2393a --- /dev/null +++ b/tests/regression/56-witness/28-mine-tutorial-ex4.8.c @@ -0,0 +1,14 @@ +// PARAM: --enable ana.int.interval --enable ana.sv-comp.functions --set ana.activated[+] unassume --set witness.yaml.unassume 28-mine-tutorial-ex4.8.yml +extern _Bool __VERIFIER_nondet_bool(); + +int main() { + int v = 0; + while (__VERIFIER_nondet_bool() == 0) { + __goblint_check(0 <= v); + __goblint_check(v <= 1); + if (v == 0) + v = 1; + // ... + } + return 0; +} diff --git a/tests/regression/56-witness/28-mine-tutorial-ex4.8.yml b/tests/regression/56-witness/28-mine-tutorial-ex4.8.yml new file mode 100644 index 0000000000..c92f0223e4 --- /dev/null +++ b/tests/regression/56-witness/28-mine-tutorial-ex4.8.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 299c2080-fb13-4879-be2b-5d2758465577 + creation_time: 2022-10-07T10:36:41Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''28-mine-tutorial-ex4.8.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''ana.sv-comp.functions'' ''--enable'' + ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' + ''--set'' ''goblint-dir'' ''.goblint-56-28''' + task: + input_files: + - 28-mine-tutorial-ex4.8.c + input_file_hashes: + 28-mine-tutorial-ex4.8.c: bd5b719b0d268dd7669edc1dd21b55a2bacab3187f26f112839093b910c91347 + data_model: LP64 + language: C + location: + file_name: 28-mine-tutorial-ex4.8.c + file_hash: bd5b719b0d268dd7669edc1dd21b55a2bacab3187f26f112839093b910c91347 + line: 6 + column: 2 + function: main + loop_invariant: + string: 0 <= v && v <= 1 + type: assertion + format: C diff --git a/tests/regression/56-witness/29-mine-tutorial-ex4.10.c b/tests/regression/56-witness/29-mine-tutorial-ex4.10.c new file mode 100644 index 0000000000..b4da1abef4 --- /dev/null +++ b/tests/regression/56-witness/29-mine-tutorial-ex4.10.c @@ -0,0 +1,14 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.apron.domain polyhedra --set ana.activated[+] unassume --set witness.yaml.unassume 29-mine-tutorial-ex4.10.yml +// Using Apron polyhedra to have no narrowing. + +int main() { + int v = 1; // Not explicitly stated in Miné's example + while (v <= 50) { + __goblint_check(1 <= v); + v += 2; + __goblint_check(v <= 52); + } + __goblint_check(51 <= v); + __goblint_check(v <= 52); + return 0; +} diff --git a/tests/regression/56-witness/29-mine-tutorial-ex4.10.yml b/tests/regression/56-witness/29-mine-tutorial-ex4.10.yml new file mode 100644 index 0000000000..c719c76c0a --- /dev/null +++ b/tests/regression/56-witness/29-mine-tutorial-ex4.10.yml @@ -0,0 +1,30 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: f04fe372-3d6e-4a80-9567-80c64bd7fd03 + creation_time: 2022-10-07T10:47:08Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''29-mine-tutorial-ex4.10.c'' + ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.apron.domain'' ''polyhedra'' + ''--enable'' ''witness.yaml.enabled'' ''--enable'' ''witness.yaml.enabled'' + ''--set'' ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' + ''goblint-dir'' ''.goblint-56-29''' + task: + input_files: + - 29-mine-tutorial-ex4.10.c + input_file_hashes: + 29-mine-tutorial-ex4.10.c: e0399cba2a1ab555c14e500606561777d5f029de891ea5bf3cfd8bda880a7ac8 + data_model: LP64 + language: C + location: + file_name: 29-mine-tutorial-ex4.10.c + file_hash: e0399cba2a1ab555c14e500606561777d5f029de891ea5bf3cfd8bda880a7ac8 + line: 6 + column: 2 + function: main + loop_invariant: + string: 1 <= v && v <= 52 + type: assertion + format: C From f3ff6493c7fb40670ad22310c8767ffd08244109 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 7 Oct 2022 15:22:45 +0300 Subject: [PATCH 101/132] Add inc-dec unassume example from thread-witnesses --- .../56-witness/30-base-unassume-inc-dec.c | 37 ++++++++++++ .../56-witness/30-base-unassume-inc-dec.yml | 58 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 tests/regression/56-witness/30-base-unassume-inc-dec.c create mode 100644 tests/regression/56-witness/30-base-unassume-inc-dec.yml diff --git a/tests/regression/56-witness/30-base-unassume-inc-dec.c b/tests/regression/56-witness/30-base-unassume-inc-dec.c new file mode 100644 index 0000000000..8ea9d3db5b --- /dev/null +++ b/tests/regression/56-witness/30-base-unassume-inc-dec.c @@ -0,0 +1,37 @@ +// PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 30-base-unassume-inc-dec.yml --set solvers.td3.side_widen always --enable ana.widen.tokens +#include + +int g = 0; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + while (1) { + pthread_mutex_lock(&A); + if (g < 10) + g++; + pthread_mutex_unlock(&A); + } + return NULL; +} + +void *t_fun2(void *arg) { + while (1) { + pthread_mutex_lock(&A); + if (g > -10) + g--; + pthread_mutex_unlock(&A); + } + return NULL; +} + +int main() { + pthread_t id, id2; + pthread_create(&id, NULL, t_fun, NULL); + pthread_create(&id2, NULL, t_fun2, NULL); + + pthread_mutex_lock(&A); + __goblint_check(-10 <= g); + __goblint_check(g <= 10); + pthread_mutex_unlock(&A); + return 0; +} diff --git a/tests/regression/56-witness/30-base-unassume-inc-dec.yml b/tests/regression/56-witness/30-base-unassume-inc-dec.yml new file mode 100644 index 0000000000..a0c6de4a33 --- /dev/null +++ b/tests/regression/56-witness/30-base-unassume-inc-dec.yml @@ -0,0 +1,58 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: e1da1c44-5406-4bce-ab4a-3597a2b36e24 + creation_time: 2022-10-07T11:52:13Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''30-base-unassume-inc-dec.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''witness.yaml.enabled'' ''--set'' + ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' + ''.goblint-56-30''' + task: + input_files: + - 30-base-unassume-inc-dec.c + input_file_hashes: + 30-base-unassume-inc-dec.c: 3df8890403a9efa4ab1c824bac7eba729e187ba93605beda5034793b99ab7bf0 + data_model: LP64 + language: C + location: + file_name: 30-base-unassume-inc-dec.c + file_hash: 3df8890403a9efa4ab1c824bac7eba729e187ba93605beda5034793b99ab7bf0 + line: 10 + column: 4 + function: t_fun + loop_invariant: + string: -10 <= g && g <= 10 + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 16a70fb8-6f51-4a2c-a2e0-a68a4baf1575 + creation_time: 2022-10-07T11:52:13Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''30-base-unassume-inc-dec.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''witness.yaml.enabled'' ''--set'' + ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' + ''.goblint-56-30''' + task: + input_files: + - 30-base-unassume-inc-dec.c + input_file_hashes: + 30-base-unassume-inc-dec.c: 3df8890403a9efa4ab1c824bac7eba729e187ba93605beda5034793b99ab7bf0 + data_model: LP64 + language: C + location: + file_name: 30-base-unassume-inc-dec.c + file_hash: 3df8890403a9efa4ab1c824bac7eba729e187ba93605beda5034793b99ab7bf0 + line: 20 + column: 4 + function: t_fun2 + loop_invariant: + string: -10 <= g && g <= 10 + type: assertion + format: C From 15e7a7ebd34e9fabdd4129e97eb86830fea8395f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 7 Oct 2022 15:27:12 +0300 Subject: [PATCH 102/132] Fix BaseInvariant indentation --- src/analyses/baseInvariant.ml | 68 +++++++++++++++++------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index e9d53eac53..bfaa0f2422 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -258,42 +258,42 @@ struct | Eq | Ne as op -> let both x = x, x in let m = ID.meet a b in - (match op, ID.to_bool c with - | Eq, Some true - | Ne, Some false -> both m (* def. equal: if they compare equal, both values must be from the meet *) - | Eq, Some false - | Ne, Some true -> (* def. unequal *) - (* Both values can not be in the meet together, but it's not sound to exclude the meet from both. + begin match op, ID.to_bool c with + | Eq, Some true + | Ne, Some false -> both m (* def. equal: if they compare equal, both values must be from the meet *) + | Eq, Some false + | Ne, Some true -> (* def. unequal *) + (* Both values can not be in the meet together, but it's not sound to exclude the meet from both. * e.g. a=[0,1], b=[1,2], meet a b = [1,1], but (a != b) does not imply a=[0,0], b=[2,2] since others are possible: a=[1,1], b=[2,2] * Only if a is a definite value, we can exclude it from b: *) - (* Used to cause inconsistent results: - interval not sufficiently refined: - inv_bin_int: unequal: (Unknown int([-31,31]),[0,1]) and (0,[0,0]); ikind: int; a': (Not {0}([-31,31]),[-2147483648,2147483647]), b': (0,[0,0]) - binop: m == 0, a': (Not {0}([-31,31]),[0,1]), b': (0,[0,0]) *) - let excl a b = - match ID.to_int a with - | Some x -> - let ex1 = ID.of_excl_list ikind [x] in - let ex2 = - (* Fix previously inconsistent results by excluding interval bounds. *) - let top_ik = ID.top_of ikind in - match ID.minimal b, ID.maximal b with - | Some lb, Some ub -> - let starting = if Z.equal lb x then ID.starting ikind (Z.add lb Z.one) else top_ik in - let ending = if Z.equal ub x then ID.ending ikind (Z.sub ub Z.one) else top_ik in - ID.meet starting ending - | _ -> - top_ik - in - ID.meet ex1 ex2 - | None -> b - in - let a' = excl b a in - let b' = excl a b in - if M.tracing then M.tracel "inv" "inv_bin_int: unequal: %a and %a; ikind: %a; a': %a, b': %a\n" ID.pretty a ID.pretty b d_ikind ikind ID.pretty a' ID.pretty b'; - meet_bin a' b' - | _, _ -> a, b - ) + (* Used to cause inconsistent results: + interval not sufficiently refined: + inv_bin_int: unequal: (Unknown int([-31,31]),[0,1]) and (0,[0,0]); ikind: int; a': (Not {0}([-31,31]),[-2147483648,2147483647]), b': (0,[0,0]) + binop: m == 0, a': (Not {0}([-31,31]),[0,1]), b': (0,[0,0]) *) + let excl a b = + match ID.to_int a with + | Some x -> + let ex1 = ID.of_excl_list ikind [x] in + let ex2 = + (* Fix previously inconsistent results by excluding interval bounds. *) + let top_ik = ID.top_of ikind in + match ID.minimal b, ID.maximal b with + | Some lb, Some ub -> + let starting = if Z.equal lb x then ID.starting ikind (Z.add lb Z.one) else top_ik in + let ending = if Z.equal ub x then ID.ending ikind (Z.sub ub Z.one) else top_ik in + ID.meet starting ending + | _ -> + top_ik + in + ID.meet ex1 ex2 + | None -> b + in + let a' = excl b a in + let b' = excl a b in + if M.tracing then M.tracel "inv" "inv_bin_int: unequal: %a and %a; ikind: %a; a': %a, b': %a\n" ID.pretty a ID.pretty b d_ikind ikind ID.pretty a' ID.pretty b'; + meet_bin a' b' + | _, _ -> a, b + end | Lt | Le | Ge | Gt as op -> let pred x = BI.sub x BI.one in let succ x = BI.add x BI.one in From d4687e4f4fcdf94d0bfcb6ff8571964af750e9fd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 7 Oct 2022 15:51:16 +0300 Subject: [PATCH 103/132] Add unassume expression example from thread-witnesses --- .../56-witness/31-base-unassume-mem-ex.c | 40 +++++++++ .../56-witness/31-base-unassume-mem-ex.yml | 87 +++++++++++++++++++ .../56-witness/32-base-unassume-ptr.c | 11 +++ .../56-witness/32-base-unassume-ptr.yml | 28 ++++++ 4 files changed, 166 insertions(+) create mode 100644 tests/regression/56-witness/31-base-unassume-mem-ex.c create mode 100644 tests/regression/56-witness/31-base-unassume-mem-ex.yml create mode 100644 tests/regression/56-witness/32-base-unassume-ptr.c create mode 100644 tests/regression/56-witness/32-base-unassume-ptr.yml diff --git a/tests/regression/56-witness/31-base-unassume-mem-ex.c b/tests/regression/56-witness/31-base-unassume-mem-ex.c new file mode 100644 index 0000000000..9a283787de --- /dev/null +++ b/tests/regression/56-witness/31-base-unassume-mem-ex.c @@ -0,0 +1,40 @@ +// PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 31-base-unassume-mem-ex.yml + +int main() { + int i, j; + int *p; + int r; // rand + i = 0; + j = 0; + if (r) + p = &i; + else + p = &j; + + switch (r) { + case 0: + __goblint_check(i == 0); // UNKNOWN (intentional by unassume) + __goblint_check(j == 0); // UNKNOWN (intentional by unassume) + __goblint_check(i >= 0); + __goblint_check(j >= 0); + __goblint_check(p == &i || p == &j); + break; + case 1: + __goblint_check(i == 0); // UNKNOWN (intentional by unassume) + __goblint_check(j == 0); // UNKNOWN (intentional by unassume) + __goblint_check(i >= 0); + __goblint_check(j >= 0); + __goblint_check(p == &i || p == &j); + break; + case 2: + __goblint_check(i == 0); // UNKNOWN (intentional by unassume) + __goblint_check(j == 0); // UNKNOWN (intentional by unassume) + __goblint_check(i >= 0); + __goblint_check(j >= 0); + __goblint_check(p == &i || p == &j); + break; + default: + break; + } + return 0; +} \ No newline at end of file diff --git a/tests/regression/56-witness/31-base-unassume-mem-ex.yml b/tests/regression/56-witness/31-base-unassume-mem-ex.yml new file mode 100644 index 0000000000..1a1c4e378c --- /dev/null +++ b/tests/regression/56-witness/31-base-unassume-mem-ex.yml @@ -0,0 +1,87 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 8a156304-0058-4a15-a934-884483183ba8 + creation_time: 2022-10-07T12:43:24Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''31-base-unassume-mem-ex.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''witness.yaml.enabled'' ''--set'' + ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' + ''.goblint-56-31''' + task: + input_files: + - 31-base-unassume-mem-ex.c + input_file_hashes: + 31-base-unassume-mem-ex.c: 33f8f073cefa79d294ce1e534a3d6db19a6d27ba83efc6b80e0407921997d659 + data_model: LP64 + language: C + location: + file_name: 31-base-unassume-mem-ex.c + file_hash: 33f8f073cefa79d294ce1e534a3d6db19a6d27ba83efc6b80e0407921997d659 + line: 16 + column: 6 + function: main + loop_invariant: + string: i >= 0 && j >= 0 + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 1d367e5b-84a7-42e3-8191-11384aeca509 + creation_time: 2022-10-07T12:43:24Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''31-base-unassume-mem-ex.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''witness.yaml.enabled'' ''--set'' + ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' + ''.goblint-56-31''' + task: + input_files: + - 31-base-unassume-mem-ex.c + input_file_hashes: + 31-base-unassume-mem-ex.c: 33f8f073cefa79d294ce1e534a3d6db19a6d27ba83efc6b80e0407921997d659 + data_model: LP64 + language: C + location: + file_name: 31-base-unassume-mem-ex.c + file_hash: 33f8f073cefa79d294ce1e534a3d6db19a6d27ba83efc6b80e0407921997d659 + line: 23 + column: 6 + function: main + loop_invariant: + string: (p == &i || p == &j) && *p >= 0 + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: a5dc6e03-3748-4e52-befc-8c846634e329 + creation_time: 2022-10-07T12:43:24Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''31-base-unassume-mem-ex.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''witness.yaml.enabled'' ''--set'' + ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' + ''.goblint-56-31''' + task: + input_files: + - 31-base-unassume-mem-ex.c + input_file_hashes: + 31-base-unassume-mem-ex.c: 33f8f073cefa79d294ce1e534a3d6db19a6d27ba83efc6b80e0407921997d659 + data_model: LP64 + language: C + location: + file_name: 31-base-unassume-mem-ex.c + file_hash: 33f8f073cefa79d294ce1e534a3d6db19a6d27ba83efc6b80e0407921997d659 + line: 30 + column: 6 + function: main + loop_invariant: + string: '*p >= 0' + type: assertion + format: C diff --git a/tests/regression/56-witness/32-base-unassume-ptr.c b/tests/regression/56-witness/32-base-unassume-ptr.c new file mode 100644 index 0000000000..b6b51d4ca0 --- /dev/null +++ b/tests/regression/56-witness/32-base-unassume-ptr.c @@ -0,0 +1,11 @@ +// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 32-base-unassume-ptr.yml +// TODO: disjunction in invariant +int main() { + int i, j; + int *p; + p = &i; + + __goblint_check(p == &i); // UNKNOWN (intentional by unassume) + __goblint_check(p == &i || p == &j); + return 0; +} diff --git a/tests/regression/56-witness/32-base-unassume-ptr.yml b/tests/regression/56-witness/32-base-unassume-ptr.yml new file mode 100644 index 0000000000..a12c5d846c --- /dev/null +++ b/tests/regression/56-witness/32-base-unassume-ptr.yml @@ -0,0 +1,28 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: d2a65147-2d04-49f8-8f27-fe3a7c8553b7 + creation_time: 2022-10-07T12:50:05Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''32-base-unassume-ptr.c'' + ''--enable'' ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--enable'' + ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' ''.goblint-56-32''' + task: + input_files: + - 32-base-unassume-ptr.c + input_file_hashes: + 32-base-unassume-ptr.c: 36ab59c15763945f1d20bc6bc4240e220286b6c8e4cef83742733bf75bc397b0 + data_model: LP64 + language: C + location: + file_name: 32-base-unassume-ptr.c + file_hash: 36ab59c15763945f1d20bc6bc4240e220286b6c8e4cef83742733bf75bc397b0 + line: 8 + column: 2 + function: main + loop_invariant: + string: p == &i || p == &j + type: assertion + format: C From a816bb1934bf449bcdd5537526e396ed85ae3d97 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 7 Oct 2022 16:52:42 +0300 Subject: [PATCH 104/132] Refine by equality disjunction in base --- src/analyses/base.ml | 4 +- src/analyses/baseInvariant.ml | 76 ++++++++++++++++++- src/cdomains/intDomain.ml | 2 + src/cdomains/intDomain.mli | 1 + ...sume-ptr.c => 32-base-unassume-lor-addr.c} | 4 +- ...-ptr.yml => 32-base-unassume-lor-addr.yml} | 8 +- .../56-witness/33-base-unassume-lor-enums.c | 10 +++ .../56-witness/33-base-unassume-lor-enums.yml | 29 +++++++ 8 files changed, 124 insertions(+), 10 deletions(-) rename tests/regression/56-witness/{32-base-unassume-ptr.c => 32-base-unassume-lor-addr.c} (69%) rename tests/regression/56-witness/{32-base-unassume-ptr.yml => 32-base-unassume-lor-addr.yml} (78%) create mode 100644 tests/regression/56-witness/33-base-unassume-lor-enums.c create mode 100644 tests/regression/56-witness/33-base-unassume-lor-enums.yml diff --git a/src/analyses/base.ml b/src/analyses/base.ml index b436156985..d3d0de84bb 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -845,7 +845,7 @@ struct v' (* Binary operators *) (* Eq/Ne when both values are equal and casted to the same type *) - | BinOp (op, (CastE (t1, e1) as c1), (CastE (t2, e2) as c2), typ) when typeSig t1 = typeSig t2 && (op = Eq || op = Ne) -> + | BinOp ((Eq | Ne) as op, (CastE (t1, e1) as c1), (CastE (t2, e2) as c2), typ) when typeSig t1 = typeSig t2 -> let a1 = eval_rv a gs st e1 in let a2 = eval_rv a gs st e2 in let (e1, e2) = binop_remove_same_casts ~extra_is_safe:(VD.equal a1 a2) ~e1 ~e2 ~t1 ~t2 ~c1 ~c2 in @@ -857,7 +857,7 @@ struct (* split nested LOr Eqs to equality pairs, if possible *) let rec split = function (* copied from above to support pointer equalities with implicit casts inserted *) - | BinOp (op, (CastE (t1, e1) as c1), (CastE (t2, e2) as c2), typ) when typeSig t1 = typeSig t2 && (op = Eq || op = Ne) -> + | BinOp (Eq, (CastE (t1, e1) as c1), (CastE (t2, e2) as c2), typ) when typeSig t1 = typeSig t2 -> Some [binop_remove_same_casts ~extra_is_safe:false ~e1 ~e2 ~t1 ~t2 ~c1 ~c2] | BinOp (Eq, arg1, arg2, _) -> Some [(arg1, arg2)] diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index bfaa0f2422..83ecd830d9 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -472,8 +472,80 @@ struct | UnOp (Neg, e, _), `Float c -> inv_exp (`Float (unop_FD Neg c)) e st | UnOp ((BNot|Neg) as op, e, _), `Int c -> inv_exp (`Int (unop_ID op c)) e st (* no equivalent for `Float, as VD.is_safe_cast fails for all float types anyways *) - | BinOp(op, CastE (t1, c1), CastE (t2, c2), t), `Int c when (op = Eq || op = Ne) && typeSig (Cilfacade.typeOf c1) = typeSig (Cilfacade.typeOf c2) && VD.is_safe_cast t1 (Cilfacade.typeOf c1) && VD.is_safe_cast t2 (Cilfacade.typeOf c2) -> - inv_exp (`Int c) (BinOp (op, c1, c2, t)) st + | BinOp((Eq | Ne) as op, CastE (t1, e1), CastE (t2, e2), t), `Int c when typeSig (Cilfacade.typeOf e1) = typeSig (Cilfacade.typeOf e2) && VD.is_safe_cast t1 (Cilfacade.typeOf e1) && VD.is_safe_cast t2 (Cilfacade.typeOf e2) -> + inv_exp (`Int c) (BinOp (op, e1, e2, t)) st + | BinOp (LOr, arg1, arg2, typ) as exp, `Int c -> + (* copied & modified from eval_rv_base... *) + let (let*) = Option.bind in + (* split nested LOr Eqs to equality pairs, if possible *) + let rec split = function + (* copied from above to support pointer equalities with implicit casts inserted *) + | BinOp (Eq, CastE (t1, e1), CastE (t2, e2), typ) when typeSig (Cilfacade.typeOf e1) = typeSig (Cilfacade.typeOf e2) && VD.is_safe_cast t1 (Cilfacade.typeOf e1) && VD.is_safe_cast t2 (Cilfacade.typeOf e2) -> (* slightly different from eval_rv_base... *) + Some [(e1, e2)] + | BinOp (Eq, arg1, arg2, _) -> + Some [(arg1, arg2)] + | BinOp (LOr, arg1, arg2, _) -> + let* s1 = split arg1 in + let* s2 = split arg2 in + Some (s1 @ s2) + | _ -> + None + in + (* find common exp from all equality pairs and list of other sides, if possible *) + let find_common = function + | [] -> assert false + | (e1, e2) :: eqs -> + let eqs_for_all_mem e = List.for_all (fun (e1, e2) -> CilType.Exp.(equal e1 e || equal e2 e)) eqs in + let eqs_map_remove e = List.map (fun (e1, e2) -> if CilType.Exp.equal e1 e then e2 else e1) eqs in + if eqs_for_all_mem e1 then + Some (e1, e2 :: eqs_map_remove e1) + else if eqs_for_all_mem e2 then + Some (e2, e1 :: eqs_map_remove e2) + else + None + in + let eqs_st = + let* eqs = split exp in + let* (e, es) = find_common eqs in + let v = eval e st in (* value of common exp *) + let vs = List.map (fun e -> eval e st) es in (* values of other sides *) + match v with + | `Address _ -> + (* get definite addrs from vs *) + let rec to_definite_ad = function + | [] -> AD.empty () + | `Address a :: vs when AD.is_definite a -> + AD.union a (to_definite_ad vs) + | _ :: vs -> + AD.top () + in + let definite_ad = to_definite_ad vs in + let c' = `Address definite_ad in + Some (inv_exp c' e st) + | `Int i -> + let ik = ID.ikind i in + let module BISet = IntDomain.BISet in + (* get definite ints from vs *) + let rec to_int_id = function + | [] -> ID.bot_of ik + | `Int i :: vs -> + begin match ID.to_int i with + | Some i' -> ID.join i (to_int_id vs) + | None -> ID.top_of ik + end + | _ :: vs -> + ID.top_of ik + in + let int_id = to_int_id vs in + let c' = `Int int_id in + Some (inv_exp c' e st) + | _ -> + None + in + begin match eqs_st with + | Some st -> st + | None -> st (* TODO: not bothering to fall back, no other case can refine LOr anyway *) + end | (BinOp (op, e1, e2, _) as e, `Float _) | (BinOp (op, e1, e2, _) as e, `Int _) -> let invert_binary_op c pretty c_int c_float = diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 532d9f2cb3..cbf507aa9e 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -287,6 +287,8 @@ struct type int_t = I.int_t type t = { v : I.t; ikind : CilType.Ikind.t } [@@deriving eq, ord, hash] + let ikind {ikind; _} = ikind + (* Helper functions *) let check_ikinds x y = if x.ikind <> y.ikind then raise (IncompatibleIKinds ("ikinds " ^ Prelude.Ana.sprint Cil.d_ikind x.ikind ^ " and " ^ Prelude.Ana.sprint Cil.d_ikind y.ikind ^ " are incompatible. Values: " ^ Prelude.Ana.sprint I.pretty x.v ^ " and " ^ Prelude.Ana.sprint I.pretty y.v)) else () let lift op x = {x with v = op x.ikind x.v } diff --git a/src/cdomains/intDomain.mli b/src/cdomains/intDomain.mli index 1c0f6e947e..2a163ef0d8 100644 --- a/src/cdomains/intDomain.mli +++ b/src/cdomains/intDomain.mli @@ -320,6 +320,7 @@ module IntDomWithDefaultIkind (I: Y) (Ik: Ikind) : Y with type t = I.t and type module IntDomTuple : sig include Z val no_interval: t -> t + val ikind: t -> ikind end val of_const: Cilint.cilint * Cil.ikind * string option -> IntDomTuple.t diff --git a/tests/regression/56-witness/32-base-unassume-ptr.c b/tests/regression/56-witness/32-base-unassume-lor-addr.c similarity index 69% rename from tests/regression/56-witness/32-base-unassume-ptr.c rename to tests/regression/56-witness/32-base-unassume-lor-addr.c index b6b51d4ca0..242fee78d4 100644 --- a/tests/regression/56-witness/32-base-unassume-ptr.c +++ b/tests/regression/56-witness/32-base-unassume-lor-addr.c @@ -1,5 +1,5 @@ -// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 32-base-unassume-ptr.yml -// TODO: disjunction in invariant +// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 32-base-unassume-lor-addr.yml +// TODO: AD refine in invariant int main() { int i, j; int *p; diff --git a/tests/regression/56-witness/32-base-unassume-ptr.yml b/tests/regression/56-witness/32-base-unassume-lor-addr.yml similarity index 78% rename from tests/regression/56-witness/32-base-unassume-ptr.yml rename to tests/regression/56-witness/32-base-unassume-lor-addr.yml index a12c5d846c..da178f8d1a 100644 --- a/tests/regression/56-witness/32-base-unassume-ptr.yml +++ b/tests/regression/56-witness/32-base-unassume-lor-addr.yml @@ -6,18 +6,18 @@ producer: name: Goblint version: heads/yaml-witness-unassume-0-g9426848d9-dirty - command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''32-base-unassume-ptr.c'' + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''32-base-unassume-lor-addr.c'' ''--enable'' ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' ''.goblint-56-32''' task: input_files: - - 32-base-unassume-ptr.c + - 32-base-unassume-lor-addr.c input_file_hashes: - 32-base-unassume-ptr.c: 36ab59c15763945f1d20bc6bc4240e220286b6c8e4cef83742733bf75bc397b0 + 32-base-unassume-lor-addr.c: 36ab59c15763945f1d20bc6bc4240e220286b6c8e4cef83742733bf75bc397b0 data_model: LP64 language: C location: - file_name: 32-base-unassume-ptr.c + file_name: 32-base-unassume-lor-addr.c file_hash: 36ab59c15763945f1d20bc6bc4240e220286b6c8e4cef83742733bf75bc397b0 line: 8 column: 2 diff --git a/tests/regression/56-witness/33-base-unassume-lor-enums.c b/tests/regression/56-witness/33-base-unassume-lor-enums.c new file mode 100644 index 0000000000..5cf341b9a5 --- /dev/null +++ b/tests/regression/56-witness/33-base-unassume-lor-enums.c @@ -0,0 +1,10 @@ +// PARAM: --enable ana.int.enums --set ana.activated[+] unassume --set witness.yaml.unassume 33-base-unassume-lor-enums.yml + +int main() { + int i; + i = 0; + + __goblint_check(i == 0); // UNKNOWN (intentional by unassume) + __goblint_check(i == 0 || i == 1 || i == 2); + return 0; +} diff --git a/tests/regression/56-witness/33-base-unassume-lor-enums.yml b/tests/regression/56-witness/33-base-unassume-lor-enums.yml new file mode 100644 index 0000000000..e98d25602f --- /dev/null +++ b/tests/regression/56-witness/33-base-unassume-lor-enums.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 8c07adbd-908d-45be-af99-943634ad66b4 + creation_time: 2022-10-07T13:43:40Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-gd4687e4f4-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''33-base-unassume-lor-enums.c'' + ''--enable'' ''ana.int.enums'' ''--enable'' ''witness.yaml.enabled'' ''--set'' + ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' + ''.goblint-56-33''' + task: + input_files: + - 33-base-unassume-lor-enums.c + input_file_hashes: + 33-base-unassume-lor-enums.c: 5014d96b45984600f1ce8a4f6f208ab856539c33e7124a71e41836a1a2cbdd40 + data_model: LP64 + language: C + location: + file_name: 33-base-unassume-lor-enums.c + file_hash: 5014d96b45984600f1ce8a4f6f208ab856539c33e7124a71e41836a1a2cbdd40 + line: 7 + column: 2 + function: main + loop_invariant: + string: i == 0 || i == 1 || i == 2 + type: assertion + format: C From bf25ca3136ca03d9aedbfa089b9fd76c67244425 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 7 Oct 2022 17:02:26 +0300 Subject: [PATCH 105/132] Refine addresses in base --- src/analyses/baseInvariant.ml | 43 +++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 83ecd830d9..b08d63ca41 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -574,25 +574,34 @@ struct | `Int c -> invert_binary_op c ID.pretty (fun ik -> ID.cast_to ik c) (fun fk -> FD.of_int fk c) | `Float c -> invert_binary_op c FD.pretty (fun ik -> FD.to_int ik c) (fun fk -> FD.cast_to fk c) | _ -> failwith "unreachable") - | Lval x, `Int _(* meet x with c *) - | Lval x, `Float _ -> (* meet x with c *) + | Lval x, (`Int _ | `Float _ | `Address _) -> (* meet x with c *) let update_lval c x c' pretty = refine_lv ctx a gs st c x c' pretty exp in let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) - (match c_typed with - | `Int c -> update_lval c x (match t with - | TPtr _ -> `Address (AD.of_int (module ID) c) - | TInt (ik, _) - | TEnum ({ekind = ik; _}, _) -> `Int (ID.cast_to ik c) - | TFloat (fk, _) -> `Float (FD.of_int fk c) - | _ -> `Int c) ID.pretty - | `Float c -> update_lval c x (match t with - (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) - | TInt (ik, _) -> `Int (FD.to_int ik c) - (* this is theoretically possible and should be handled correctly, however i can't imagine an actual piece of c code producing this?! *) - | TEnum ({ekind = ik; _}, _) -> `Int (FD.to_int ik c) - | TFloat (fk, _) -> `Float (FD.cast_to fk c) - | _ -> `Float c) FD.pretty - | _ -> failwith "unreachable") + begin match c_typed with + | `Int c -> + let c' = match t with + | TPtr _ -> `Address (AD.of_int (module ID) c) + | TInt (ik, _) + | TEnum ({ekind = ik; _}, _) -> `Int (ID.cast_to ik c) + | TFloat (fk, _) -> `Float (FD.of_int fk c) + | _ -> `Int c + in + update_lval c x c' ID.pretty + | `Float c -> + let c' = match t with + (* | TPtr _ -> ..., pointer conversion from/to float is not supported *) + | TInt (ik, _) -> `Int (FD.to_int ik c) + (* this is theoretically possible and should be handled correctly, however i can't imagine an actual piece of c code producing this?! *) + | TEnum ({ekind = ik; _}, _) -> `Int (FD.to_int ik c) + | TFloat (fk, _) -> `Float (FD.cast_to fk c) + | _ -> `Float c + in + update_lval c x c' FD.pretty + | `Address c -> + let c' = c_typed in (* TODO: need any of the type-matching nonsense? *) + update_lval c x c' AD.pretty + | _ -> assert false + end | Const _ , _ -> st (* nothing to do *) | CastE ((TFloat (_, _)), e), `Float c -> (match Cilfacade.typeOf e, FD.get_fkind c with From 8b307fe1f57d0def7f969fb56a9206f6e96a2b5e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 10 Oct 2022 16:24:01 +0300 Subject: [PATCH 106/132] Fix unassume inc-dec example on lockset-based privatizations --- src/analyses/mCP.ml | 39 ++++++++----- src/domains/events.ml | 17 +++++- .../34-base-unassume-inc-dec-traces.c | 37 ++++++++++++ .../34-base-unassume-inc-dec-traces.yml | 58 +++++++++++++++++++ 4 files changed, 134 insertions(+), 17 deletions(-) create mode 100644 tests/regression/56-witness/34-base-unassume-inc-dec-traces.c create mode 100644 tests/regression/56-witness/34-base-unassume-inc-dec-traces.yml diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 8f7eda2f47..dbe69bd898 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -183,11 +183,11 @@ struct (* Do split-specific emits before general emits. [emits] and [do_emits] are in reverse order. [emits'] is in normal order. *) - ctx.split (do_emits ctx (emits @ List.rev emits') nv) [] + ctx.split (do_emits ctx (emits @ List.rev emits') nv false) [] in iter (uncurry split_one) xs - and do_emits ctx emits xs = + and do_emits ctx emits xs dead = let octx = ctx in let ctx_with_local ctx local' = (* let rec ctx' = @@ -217,15 +217,22 @@ struct let octx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "do_emits" ~splits ~post_all octx'' n od in n, repr @@ S.event ctx' e octx' in + if M.tracing then M.traceli "event" "%a\n before: %a\n" Events.pretty e D.pretty ctx.local; let d, q = map_deadcode f @@ spec_list2 ctx.local octx.local in - if M.tracing then M.tracel "event" "%a\n before: %a\n after:%a\n" Events.pretty e D.pretty ctx.local D.pretty d; + if M.tracing then M.traceu "event" "%a\n after:%a\n" Events.pretty e D.pretty d; do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else ctx_with_local ctx d in if M.tracing then M.traceli "event" "do_emits:\n"; + let emits = + if dead then + List.filter Events.emit_on_deadcode emits + else + emits + in (* [emits] is in reverse order. *) let ctx' = List.fold_left do_emit (ctx_with_local ctx xs) (List.rev emits) in if M.tracing then M.traceu "event" "\n"; @@ -245,7 +252,7 @@ struct do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d (* Explicitly polymorphic type required here for recursive GADT call in ask. *) @@ -374,7 +381,7 @@ struct do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d @@ -392,7 +399,7 @@ struct do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d let body (ctx:(D.t, G.t, C.t, V.t) ctx) f = @@ -409,7 +416,7 @@ struct do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d let return (ctx:(D.t, G.t, C.t, V.t) ctx) e f = @@ -426,7 +433,7 @@ struct do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d @@ -444,7 +451,7 @@ struct do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d let skip (ctx:(D.t, G.t, C.t, V.t) ctx) = @@ -461,7 +468,7 @@ struct do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d let special (ctx:(D.t, G.t, C.t, V.t) ctx) r f a = @@ -478,7 +485,7 @@ struct do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d let sync (ctx:(D.t, G.t, C.t, V.t) ctx) reason = @@ -495,7 +502,7 @@ struct do_sideg ctx !sides; do_spawns ctx !spawns; do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d let enter (ctx:(D.t, G.t, C.t, V.t) ctx) r f a = @@ -537,7 +544,7 @@ struct let d, q = map_deadcode f @@ List.rev @@ spec_list3_rev_acc [] ctx.local fc fd in do_sideg ctx !sides; do_spawns ctx !spawns; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d let threadenter (ctx:(D.t, G.t, C.t, V.t) ctx) lval f a = @@ -551,7 +558,7 @@ struct let css = map f @@ spec_list ctx.local in do_sideg ctx !sides; (* TODO: this do_emits is now different from everything else *) - map (do_emits ctx !emits) @@ map topo_sort_an @@ n_cartesian_product css + map (fun d -> do_emits ctx !emits d false) @@ map topo_sort_an @@ n_cartesian_product css let threadspawn (ctx:(D.t, G.t, C.t, V.t) ctx) lval f a fctx = let sides = ref [] in @@ -565,6 +572,6 @@ struct in let d, q = map_deadcode f @@ spec_list2 ctx.local fctx.local in do_sideg ctx !sides; - let d = do_emits ctx !emits d in + let d = do_emits ctx !emits d q in if q then raise Deadcode else d end diff --git a/src/domains/events.ml b/src/domains/events.ml index e0a2422ddf..bc4d8b1248 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -8,10 +8,25 @@ type t = | SplitBranch of exp * bool (** Used to simulate old branch-based split. *) | AssignSpawnedThread of lval * ThreadIdDomain.Thread.t (** Assign spawned thread's ID to lval. *) | Access of {exp: CilType.Exp.t; lvals: Queries.LS.t; kind: AccessKind.t; reach: bool} - | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) + | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) (* TODO: unused *) | UpdateExpSplit of exp (** Used by expsplit analysis to evaluate [exp] on post-state. *) | Unassume of {exp: CilType.Exp.t; uuids: string list} +(** Should event be emitted after transfer function raises [Deadcode]? *) +let emit_on_deadcode = function + | Unlock _ (* Privatization must still publish. *) + | Escape _ (* Privatization must still handle escapes. *) + | EnterMultiThreaded (* Privatization must still publish. *) + | Access _ -> (* Protection and races must still consider access. *) + true + | Lock _ (* Doesn't need to publish. *) + | SplitBranch _ (* only emitted in split, which is never dead. *) + | AssignSpawnedThread _ (* Happens only after live thread spawn. *) + | Assign _ + | UpdateExpSplit _ (* Pointless to split on dead. *) + | Unassume _ -> (* Avoid spurious writes. *) + false + let pretty () = function | Lock m -> dprintf "Lock %a" LockDomain.Lockset.Lock.pretty m | Unlock m -> dprintf "Unlock %a" LockDomain.Addr.pretty m diff --git a/tests/regression/56-witness/34-base-unassume-inc-dec-traces.c b/tests/regression/56-witness/34-base-unassume-inc-dec-traces.c new file mode 100644 index 0000000000..4b68e1c07b --- /dev/null +++ b/tests/regression/56-witness/34-base-unassume-inc-dec-traces.c @@ -0,0 +1,37 @@ +// PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 34-base-unassume-inc-dec-traces.yml --set solvers.td3.side_widen always --enable ana.widen.tokens --set ana.base.privatization write+lock +#include + +int g = 0; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + while (1) { + pthread_mutex_lock(&A); + if (g < 10) + g++; + pthread_mutex_unlock(&A); + } + return NULL; +} + +void *t_fun2(void *arg) { + while (1) { + pthread_mutex_lock(&A); + if (g > -10) + g--; + pthread_mutex_unlock(&A); + } + return NULL; +} + +int main() { + pthread_t id, id2; + pthread_create(&id, NULL, t_fun, NULL); + pthread_create(&id2, NULL, t_fun2, NULL); + + pthread_mutex_lock(&A); + __goblint_check(-10 <= g); + __goblint_check(g <= 10); + pthread_mutex_unlock(&A); + return 0; +} diff --git a/tests/regression/56-witness/34-base-unassume-inc-dec-traces.yml b/tests/regression/56-witness/34-base-unassume-inc-dec-traces.yml new file mode 100644 index 0000000000..e159e57c44 --- /dev/null +++ b/tests/regression/56-witness/34-base-unassume-inc-dec-traces.yml @@ -0,0 +1,58 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: e1da1c44-5406-4bce-ab4a-3597a2b36e24 + creation_time: 2022-10-07T11:52:13Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''34-base-unassume-inc-dec-traces.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''witness.yaml.enabled'' ''--set'' + ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' + ''.goblint-56-30''' + task: + input_files: + - 34-base-unassume-inc-dec-traces.c + input_file_hashes: + 34-base-unassume-inc-dec-traces.c: 3df8890403a9efa4ab1c824bac7eba729e187ba93605beda5034793b99ab7bf0 + data_model: LP64 + language: C + location: + file_name: 34-base-unassume-inc-dec-traces.c + file_hash: 3df8890403a9efa4ab1c824bac7eba729e187ba93605beda5034793b99ab7bf0 + line: 10 + column: 4 + function: t_fun + loop_invariant: + string: -10 <= g && g <= 10 + type: assertion + format: C +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 16a70fb8-6f51-4a2c-a2e0-a68a4baf1575 + creation_time: 2022-10-07T11:52:13Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g9426848d9-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''34-base-unassume-inc-dec-traces.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''witness.yaml.enabled'' ''--set'' + ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' + ''.goblint-56-30''' + task: + input_files: + - 34-base-unassume-inc-dec-traces.c + input_file_hashes: + 34-base-unassume-inc-dec-traces.c: 3df8890403a9efa4ab1c824bac7eba729e187ba93605beda5034793b99ab7bf0 + data_model: LP64 + language: C + location: + file_name: 34-base-unassume-inc-dec-traces.c + file_hash: 3df8890403a9efa4ab1c824bac7eba729e187ba93605beda5034793b99ab7bf0 + line: 20 + column: 4 + function: t_fun2 + loop_invariant: + string: -10 <= g && g <= 10 + type: assertion + format: C From 3ea59f7dfcc316f4b42781c70369e1a35ee2d348 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 12 Oct 2022 14:05:04 +0300 Subject: [PATCH 107/132] Add option solvers.td3.remove-wpoint --- src/solvers/td3.ml | 3 ++- src/util/options.schema.json | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/solvers/td3.ml b/src/solvers/td3.ml index 20dd88241a..a9203be48d 100644 --- a/src/solvers/td3.ml +++ b/src/solvers/td3.ml @@ -99,6 +99,7 @@ module WP = let stable = data.stable in let narrow_reuse = GobConfig.get_bool "solvers.td3.narrow-reuse" in + let remove_wpoint = GobConfig.get_bool "solvers.td3.remove-wpoint" in let side_dep = data.side_dep in let side_infl = data.side_infl in @@ -215,7 +216,7 @@ module WP = HM.remove stable x; HM.remove superstable x; (solve[@tailcall]) ~reuse_eq:new_eq x Narrow - ) else if not space && (not term || phase = Narrow) then ( (* this makes e.g. nested loops precise, ex. tests/regression/34-localization/01-nested.c - if we do not remove wpoint, the inner loop head will stay a wpoint and widen the outer loop variable. *) + ) else if remove_wpoint && not space && (not term || phase = Narrow) then ( (* this makes e.g. nested loops precise, ex. tests/regression/34-localization/01-nested.c - if we do not remove wpoint, the inner loop head will stay a wpoint and widen the outer loop variable. *) if tracing then trace "sol2" "solve removing wpoint %a (%b)\n" S.Var.pretty_trace x (HM.mem wpoint x); HM.remove wpoint x ) diff --git a/src/util/options.schema.json b/src/util/options.schema.json index 4f9279d7ed..fda204a1f4 100644 --- a/src/util/options.schema.json +++ b/src/util/options.schema.json @@ -1954,6 +1954,12 @@ "type": "boolean", "default": true }, + "remove-wpoint": { + "title": "solvers.td3.remove-wpoint", + "description": "Remove widening points after narrowing phase. Enables a form of local restarting which increases precision of nested loops.", + "type": "boolean", + "default": true + }, "restart": { "title": "solvers.td3.restart", "type": "object", From 20763b0877cbdbfc9af7e9f0d5b88912096775cb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 12 Oct 2022 14:05:47 +0300 Subject: [PATCH 108/132] Add Halbwachs-Henry unassume examples --- tests/regression/56-witness/35-hh-ex1b.c | 17 +++++++++++++ tests/regression/56-witness/35-hh-ex1b.yml | 29 ++++++++++++++++++++++ tests/regression/56-witness/36-hh-ex2b.c | 19 ++++++++++++++ tests/regression/56-witness/36-hh-ex2b.yml | 29 ++++++++++++++++++++++ tests/regression/56-witness/37-hh-ex3.c | 22 ++++++++++++++++ tests/regression/56-witness/37-hh-ex3.yml | 29 ++++++++++++++++++++++ 6 files changed, 145 insertions(+) create mode 100644 tests/regression/56-witness/35-hh-ex1b.c create mode 100644 tests/regression/56-witness/35-hh-ex1b.yml create mode 100644 tests/regression/56-witness/36-hh-ex2b.c create mode 100644 tests/regression/56-witness/36-hh-ex2b.yml create mode 100644 tests/regression/56-witness/37-hh-ex3.c create mode 100644 tests/regression/56-witness/37-hh-ex3.yml diff --git a/tests/regression/56-witness/35-hh-ex1b.c b/tests/regression/56-witness/35-hh-ex1b.c new file mode 100644 index 0000000000..945561a291 --- /dev/null +++ b/tests/regression/56-witness/35-hh-ex1b.c @@ -0,0 +1,17 @@ +// PARAM: --enable ana.int.interval --disable solvers.td3.remove-wpoint --set ana.activated[+] unassume --set witness.yaml.unassume 35-hh-ex1b.yml + +int main() { + int i = 0; + while (i < 100) { + int j = 0; + while (j < 100) { + j++; + __goblint_check(j <= 100); + } + __goblint_check(j == 100); + i++; + __goblint_check(i <= 100); + } + __goblint_check(i == 100); + return 0; +} diff --git a/tests/regression/56-witness/35-hh-ex1b.yml b/tests/regression/56-witness/35-hh-ex1b.yml new file mode 100644 index 0000000000..0e2db4aeac --- /dev/null +++ b/tests/regression/56-witness/35-hh-ex1b.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 1a354f1e-76e8-40e9-808a-8d5efbe35496 + creation_time: 2022-10-12T10:45:49Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g8b307fe1f-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''35-hh-ex1b.c'' + ''--enable'' ''ana.int.interval'' ''--disable'' ''solvers.td3.remove-wpoint'' + ''--enable'' ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--enable'' + ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' ''.goblint-56-35''' + task: + input_files: + - 35-hh-ex1b.c + input_file_hashes: + 35-hh-ex1b.c: 9fe07d5f950140350848bae4342d9d1b7c6c9f625f6985746089a36a37243600 + data_model: LP64 + language: C + location: + file_name: 35-hh-ex1b.c + file_hash: 9fe07d5f950140350848bae4342d9d1b7c6c9f625f6985746089a36a37243600 + line: 7 + column: 2 + function: main + loop_invariant: + string: 0 <= i && i <= 99 + type: assertion + format: C diff --git a/tests/regression/56-witness/36-hh-ex2b.c b/tests/regression/56-witness/36-hh-ex2b.c new file mode 100644 index 0000000000..3ae359756e --- /dev/null +++ b/tests/regression/56-witness/36-hh-ex2b.c @@ -0,0 +1,19 @@ +// PARAM: --enable ana.int.interval --enable ana.sv-comp.functions --set ana.activated[+] unassume --set witness.yaml.unassume 36-hh-ex2b.yml +extern _Bool __VERIFIER_nondet_bool(); + +int main() { + int n = 0; + while (1) { + __goblint_check(n <= 60); + if (__VERIFIER_nondet_bool()) { + if (n < 60) { + n++; + } + else { + __goblint_check(n == 60); + n = 0; + } + } + } + return 0; +} diff --git a/tests/regression/56-witness/36-hh-ex2b.yml b/tests/regression/56-witness/36-hh-ex2b.yml new file mode 100644 index 0000000000..d2a7218a28 --- /dev/null +++ b/tests/regression/56-witness/36-hh-ex2b.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 9589b9d4-edce-4043-8413-279703946762 + creation_time: 2022-10-12T10:38:16Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g8b307fe1f + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''36-hh-ex2b.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''ana.sv-comp.functions'' ''--enable'' + ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' + ''--set'' ''goblint-dir'' ''.goblint-56-36''' + task: + input_files: + - 36-hh-ex2b.c + input_file_hashes: + 36-hh-ex2b.c: e4a0ecee56af00cc83ce75411061e74f80edce8c562310a7ea11499eb606d133 + data_model: LP64 + language: C + location: + file_name: 36-hh-ex2b.c + file_hash: e4a0ecee56af00cc83ce75411061e74f80edce8c562310a7ea11499eb606d133 + line: 6 + column: 2 + function: main + loop_invariant: + string: 0 <= n && n <= 60 + type: assertion + format: C diff --git a/tests/regression/56-witness/37-hh-ex3.c b/tests/regression/56-witness/37-hh-ex3.c new file mode 100644 index 0000000000..d9861d387b --- /dev/null +++ b/tests/regression/56-witness/37-hh-ex3.c @@ -0,0 +1,22 @@ +// SKIP PARAM: --set ana.activated[+] apron --disable solvers.td3.remove-wpoint --set ana.activated[+] unassume --set witness.yaml.unassume 37-hh-ex3.yml + +int main() { + int i = 0; + while (i < 4) { + int j = 0; + while (j < 4) { + i++; + j++; + __goblint_check(0 <= j); + __goblint_check(j <= i); + __goblint_check(i <= j + 3); + __goblint_check(j <= 4); + } + __goblint_check(0 <= j); + __goblint_check(j <= i); + __goblint_check(i <= j + 3); + __goblint_check(j <= 4); + i = i - j + 1; + } + return 0; +} diff --git a/tests/regression/56-witness/37-hh-ex3.yml b/tests/regression/56-witness/37-hh-ex3.yml new file mode 100644 index 0000000000..77ec224d97 --- /dev/null +++ b/tests/regression/56-witness/37-hh-ex3.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: d834761a-d0d7-4fea-bf42-2ff2b9a19143 + creation_time: 2022-10-12T10:59:25Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g8b307fe1f-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''37-hh-ex3.c'' + ''--set'' ''ana.activated[+]'' ''apron'' ''--disable'' ''solvers.td3.remove-wpoint'' + ''--enable'' ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--enable'' + ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' ''.goblint-56-37''' + task: + input_files: + - 37-hh-ex3.c + input_file_hashes: + 37-hh-ex3.c: 9c984e89a790b595d2b37ca8a05e5967a15130592cb2567fac2fae4aff668a4f + data_model: LP64 + language: C + location: + file_name: 37-hh-ex3.c + file_hash: 9c984e89a790b595d2b37ca8a05e5967a15130592cb2567fac2fae4aff668a4f + line: 7 + column: 4 + function: main + loop_invariant: + string: 0 <= i && i <= 3 && j == 0 + type: assertion + format: C From 811f2aff3066787b266facd69f63970dc094b520 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 12 Oct 2022 14:32:36 +0300 Subject: [PATCH 109/132] Add Boutonnet-Halbwachs unassume examples --- tests/regression/56-witness/38-bh-ex3.c | 34 +++++++++++++++++++ tests/regression/56-witness/38-bh-ex3.yml | 29 ++++++++++++++++ tests/regression/56-witness/39-bh-ex-add.c | 34 +++++++++++++++++++ tests/regression/56-witness/39-bh-ex-add.yml | 29 ++++++++++++++++ tests/regression/56-witness/40-bh-ex1-poly.c | 22 ++++++++++++ .../regression/56-witness/40-bh-ex1-poly.yml | 29 ++++++++++++++++ 6 files changed, 177 insertions(+) create mode 100644 tests/regression/56-witness/38-bh-ex3.c create mode 100644 tests/regression/56-witness/38-bh-ex3.yml create mode 100644 tests/regression/56-witness/39-bh-ex-add.c create mode 100644 tests/regression/56-witness/39-bh-ex-add.yml create mode 100644 tests/regression/56-witness/40-bh-ex1-poly.c create mode 100644 tests/regression/56-witness/40-bh-ex1-poly.yml diff --git a/tests/regression/56-witness/38-bh-ex3.c b/tests/regression/56-witness/38-bh-ex3.c new file mode 100644 index 0000000000..1cf314533d --- /dev/null +++ b/tests/regression/56-witness/38-bh-ex3.c @@ -0,0 +1,34 @@ +// PARAM: --enable ana.int.interval --enable ana.sv-comp.functions --set ana.activated[+] unassume --set witness.yaml.unassume 38-bh-ex3.yml +extern _Bool __VERIFIER_nondet_bool(); + +int main() { + int m = 0; + int n = 0; + while (1) { + __goblint_check(m <= 60); + __goblint_check(n <= 60); + if (__VERIFIER_nondet_bool()) { + if (__VERIFIER_nondet_bool()) { + if (m < 60) { + m++; + } + else { + __goblint_check(m == 60); + m = 0; + } + } + } + else { + if (__VERIFIER_nondet_bool()) { + if (n < 60) { + n++; + } + else { + __goblint_check(n == 60); + n = 0; + } + } + } + } + return 0; +} diff --git a/tests/regression/56-witness/38-bh-ex3.yml b/tests/regression/56-witness/38-bh-ex3.yml new file mode 100644 index 0000000000..2e489e1f17 --- /dev/null +++ b/tests/regression/56-witness/38-bh-ex3.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: bd436e09-244f-45db-a5f4-9337ca997424 + creation_time: 2022-10-12T11:13:15Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g8b307fe1f-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''38-bh-ex3.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''ana.sv-comp.functions'' ''--enable'' + ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' + ''--set'' ''goblint-dir'' ''.goblint-56-38''' + task: + input_files: + - 38-bh-ex3.c + input_file_hashes: + 38-bh-ex3.c: 9d2cb05c2decf8564defade7194c336478ec5ae4be1ee79202269ff09830dada + data_model: LP64 + language: C + location: + file_name: 38-bh-ex3.c + file_hash: 9d2cb05c2decf8564defade7194c336478ec5ae4be1ee79202269ff09830dada + line: 7 + column: 2 + function: main + loop_invariant: + string: 0 <= m && m <= 60 && 0 <= n && n <= 60 + type: assertion + format: C diff --git a/tests/regression/56-witness/39-bh-ex-add.c b/tests/regression/56-witness/39-bh-ex-add.c new file mode 100644 index 0000000000..0c6c23426a --- /dev/null +++ b/tests/regression/56-witness/39-bh-ex-add.c @@ -0,0 +1,34 @@ +// PARAM: --enable ana.int.interval --enable ana.sv-comp.functions --set ana.activated[+] unassume --set witness.yaml.unassume 39-bh-ex-add.yml +extern _Bool __VERIFIER_nondet_bool(); + +int main() { + int m = 0; + int n = 0; + while (1) { + __goblint_check(m <= 60); + __goblint_check(n <= 60); + if (__VERIFIER_nondet_bool()) { + if (__VERIFIER_nondet_bool()) { + if (m < 60) { + m++; + } + else { + __goblint_check(m == 60); + m = 0; + } + } + } + if (__VERIFIER_nondet_bool()) { + if (__VERIFIER_nondet_bool()) { + if (n < 60) { + n++; + } + else { + __goblint_check(n == 60); + n = 0; + } + } + } + } + return 0; +} diff --git a/tests/regression/56-witness/39-bh-ex-add.yml b/tests/regression/56-witness/39-bh-ex-add.yml new file mode 100644 index 0000000000..29d3006276 --- /dev/null +++ b/tests/regression/56-witness/39-bh-ex-add.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: bd436e09-244f-45db-a5f4-9337ca997424 + creation_time: 2022-10-12T11:13:15Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g8b307fe1f-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''39-bh-ex-add.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''ana.sv-comp.functions'' ''--enable'' + ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' + ''--set'' ''goblint-dir'' ''.goblint-56-38''' + task: + input_files: + - 39-bh-ex-add.c + input_file_hashes: + 39-bh-ex-add.c: 9d2cb05c2decf8564defade7194c336478ec5ae4be1ee79202269ff09830dada + data_model: LP64 + language: C + location: + file_name: 39-bh-ex-add.c + file_hash: 9d2cb05c2decf8564defade7194c336478ec5ae4be1ee79202269ff09830dada + line: 7 + column: 2 + function: main + loop_invariant: + string: 0 <= m && m <= 60 && 0 <= n && n <= 60 + type: assertion + format: C diff --git a/tests/regression/56-witness/40-bh-ex1-poly.c b/tests/regression/56-witness/40-bh-ex1-poly.c new file mode 100644 index 0000000000..47e1c41913 --- /dev/null +++ b/tests/regression/56-witness/40-bh-ex1-poly.c @@ -0,0 +1,22 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.apron.domain polyhedra --set ana.activated[+] unassume --set witness.yaml.unassume 40-bh-ex1-poly.yml --set sem.int.signed_overflow assume_none +// TODO: why need to assume no overflow? +int main() { + int i = 0; + while (i < 4) { + int j = 0; + while (j < 3) { + i++; + j += 2; + __goblint_check(0 <= j); + __goblint_check(j <= 2 * i); + __goblint_check(2 * i <= j + 6); + __goblint_check(j <= 4); + } + __goblint_check(0 <= j); + __goblint_check(j <= 2 * i); + __goblint_check(2 * i <= j + 6); + __goblint_check(j <= 4); + i = i - j / 2 + 1; + } + return 0; +} diff --git a/tests/regression/56-witness/40-bh-ex1-poly.yml b/tests/regression/56-witness/40-bh-ex1-poly.yml new file mode 100644 index 0000000000..0b06c24e90 --- /dev/null +++ b/tests/regression/56-witness/40-bh-ex1-poly.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 6ee08942-60e6-4291-a9fc-ff6f2744ef4b + creation_time: 2022-10-12T11:19:46Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g8b307fe1f-dirty + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''40-bh-ex1-poly.c'' + ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.apron.domain'' ''polyhedra'' + ''--enable'' ''witness.yaml.enabled'' ''--set'' ''dbg.debug'' ''true'' ''--enable'' + ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' ''.goblint-56-40''' + task: + input_files: + - 40-bh-ex1-poly.c + input_file_hashes: + 40-bh-ex1-poly.c: 34f781dcae089ecb6b7b2811027395fcb501b8477b7e5016f7b38081724bea28 + data_model: LP64 + language: C + location: + file_name: 40-bh-ex1-poly.c + file_hash: 34f781dcae089ecb6b7b2811027395fcb501b8477b7e5016f7b38081724bea28 + line: 7 + column: 4 + function: main + loop_invariant: + string: 0 <= i && i <= 3 && j == 0 + type: assertion + format: C From 4b04fa1bc6d86a7cb94333b683f9d7fa3f3770c4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 12 Oct 2022 16:51:58 +0300 Subject: [PATCH 110/132] Add Amato-Scozzari unassume example Their hh already exists as hh-ex3. Their nested already exists as hh-ex1b. Their nested2 already succeeds with TD3. --- tests/regression/56-witness/41-as-hybrid.c | 17 ++++++++++++ tests/regression/56-witness/41-as-hybrid.yml | 29 ++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/regression/56-witness/41-as-hybrid.c create mode 100644 tests/regression/56-witness/41-as-hybrid.yml diff --git a/tests/regression/56-witness/41-as-hybrid.c b/tests/regression/56-witness/41-as-hybrid.c new file mode 100644 index 0000000000..134012d2f8 --- /dev/null +++ b/tests/regression/56-witness/41-as-hybrid.c @@ -0,0 +1,17 @@ +// PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 41-as-hybrid.yml + +int main() { + int i = 0; + while (1) { + i++; + int j = 0; + while (j < 10) { + __goblint_check(0 <= i); + __goblint_check(i <= 10); + j++; + } + if (i > 9) + i = 0; + } + return 0; +} diff --git a/tests/regression/56-witness/41-as-hybrid.yml b/tests/regression/56-witness/41-as-hybrid.yml new file mode 100644 index 0000000000..8eac9f75f7 --- /dev/null +++ b/tests/regression/56-witness/41-as-hybrid.yml @@ -0,0 +1,29 @@ +- entry_type: loop_invariant + metadata: + format_version: "0.1" + uuid: 71b761c6-4f85-405e-8ee6-9a3a8f743513 + creation_time: 2022-10-12T11:50:43Z + producer: + name: Goblint + version: heads/yaml-witness-unassume-0-g811f2aff3 + command_line: '''/home/simmo/dev/goblint/sv-comp/goblint/goblint'' ''41-as-hybrid.c'' + ''--enable'' ''ana.int.interval'' ''--enable'' ''witness.yaml.enabled'' ''--set'' + ''dbg.debug'' ''true'' ''--enable'' ''dbg.timing.enabled'' ''--set'' ''goblint-dir'' + ''.goblint-56-41''' + task: + input_files: + - 41-as-hybrid.c + input_file_hashes: + 41-as-hybrid.c: 74adb6d33dd35bafe5415bc407a94fa277812ad9921726a7c1f5ee00d3a39af6 + data_model: LP64 + language: C + location: + file_name: 41-as-hybrid.c + file_hash: 74adb6d33dd35bafe5415bc407a94fa277812ad9921726a7c1f5ee00d3a39af6 + line: 5 + column: 2 + function: main + loop_invariant: + string: 0 <= i && i <= 9 + type: assertion + format: C From 26560c2a89e5bfc170ec9df6ce1db2855af9b129 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 8 Nov 2022 11:45:35 +0200 Subject: [PATCH 111/132] Deduplicate BaseInvariant refine_lv_fallback --- src/analyses/base.ml | 70 +++++------------------------------ src/analyses/baseInvariant.ml | 36 +++++++++++++++++- 2 files changed, 45 insertions(+), 61 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 289be6dbca..f9da5c8ae9 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1573,35 +1573,6 @@ struct let eval_rv_address = eval_rv_address let eval_lv = eval_lv - let apply_invariant oldv newv = - match oldv, newv with - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o && AD.mem (Addr.unknown_ptr ()) n -> *) - (* `Address (AD.join o n) *) - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o -> `Address n *) - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) n -> `Address o *) - | _ -> VD.meet oldv newv - - let refine_lv_fallback ctx a gs st lval value tv = - if M.tracing then M.tracec "invariant" "Restricting %a with %a\n" d_lval lval VD.pretty value; - let addr = eval_lv a gs st lval in - if (AD.is_top addr) then st - else - let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) - let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in - let t_lval = Cilfacade.typeOfLval lval in - let state_with_excluded = set a gs st addr t_lval value ~invariant:true ~ctx in - let value = get a gs state_with_excluded addr None in - let new_val = apply_invariant oldval value in - if M.tracing then M.traceu "invariant" "New value is %a\n" VD.pretty new_val; - (* make that address meet the invariant, i.e exclusion sets will be joined *) - if is_some_bot new_val then ( - if M.tracing then M.tracel "branch" "C The branch %B is dead!\n" tv; - raise Analyses.Deadcode - ) - else if VD.is_bot new_val - then set a gs st addr t_lval value ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) - else set a gs st addr t_lval new_val ~invariant:true ~ctx (* no *_raw because this is not a real assignment *) - let refine_lv ctx a gs st c x c' pretty exp = let eval e st = eval_rv a gs st e in let set' lval v st = set a gs st (eval_lv a gs st lval) (Cilfacade.typeOfLval lval) v ~invariant:true ~ctx in @@ -1629,6 +1600,11 @@ struct set' x v st ) + let get a gs st addrs exp = get a gs st addrs exp + let set a ~ctx gs st lval lval_type value = set a ~ctx ~invariant:true gs st lval lval_type value + + let map_oldval oldval _ = oldval + let id_meet_down ~old ~c = ID.meet old c let fd_meet_down ~old ~c = FD.meet old c end @@ -2392,39 +2368,8 @@ struct let oa = Analyses.ask_of_ctx octx let ost = octx.local - let apply_invariant oldv newv = - match oldv, newv with - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o && AD.mem (Addr.unknown_ptr ()) n -> *) - (* `Address (AD.join o n) *) - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o -> `Address n *) - (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) n -> `Address o *) - | _ -> VD.meet oldv newv - (* all updates happen in ctx with top values *) - let refine_lv_fallback ctx a gs st lval value tv = - if M.tracing then M.tracec "invariant" "Restricting %a with %a\n" d_lval lval VD.pretty value; - let addr = eval_lv oa gs ost lval in - if (AD.is_top addr) then st - else - let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) - let t_lval = Cilfacade.typeOfLval lval in - let oldval = if VD.is_bot oldval then VD.top_value t_lval else oldval in - let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in - let state_with_excluded = set a gs st addr t_lval value ~invariant:false ~ctx in (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) - let value = get a gs state_with_excluded addr None in - let new_val = apply_invariant oldval value in - if M.tracing then M.traceu "invariant" "New value is %a\n" VD.pretty new_val; - (* make that address meet the invariant, i.e exclusion sets will be joined *) - if is_some_bot new_val then ( - if M.tracing then M.tracel "branch" "C The branch %B is dead!\n" tv; - raise Analyses.Deadcode - ) - else if VD.is_bot new_val - (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) - then set a gs st addr t_lval value ~invariant:false ~ctx (* no *_raw because this is not a real assignment *) - else set a gs st addr t_lval new_val ~invariant:false ~ctx (* no *_raw because this is not a real assignment *) - let refine_lv ctx a gs st c x c' pretty exp = let eval_rv_lval lv st = (* old: *) @@ -2515,6 +2460,11 @@ struct (* all evals happen in octx with non-top values *) (* must be down here to make evals in refines call the real ones *) + let get a gs st addrs exp = get a gs st addrs exp + let set a ~ctx gs st lval lval_type value = set a ~ctx ~invariant:false gs st lval lval_type value (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) + + let map_oldval oldval t_lval = + if VD.is_bot oldval then VD.top_value t_lval else oldval let eval_rv a gs st e = eval_rv oa gs ost e let eval_rv_address a gs st e = eval_rv_address oa gs ost e diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 7a487ddcbc..dd41c7bea8 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -18,7 +18,11 @@ sig val eval_rv_address: Queries.ask -> (V.t -> G.t) -> D.t -> exp -> VD.t val eval_lv: Queries.ask -> (V.t -> G.t) -> D.t -> lval -> AD.t - val refine_lv_fallback: (D.t, G.t, _, V.t) Analyses.ctx -> Queries.ask -> (V.t -> G.t) -> D.t -> lval -> VD.t -> bool -> D.t + val get: Queries.ask -> (V.t -> G.t) -> D.t -> AD.t -> exp option -> VD.t + val set: Queries.ask -> ctx:(D.t, G.t, _, V.t) Analyses.ctx -> (V.t -> G.t) -> D.t -> AD.t -> typ -> VD.t -> D.t + + val map_oldval: VD.t -> typ -> VD.t + val refine_lv: (D.t, G.t, _, V.t) Analyses.ctx -> Queries.ask -> (V.t -> G.t) -> D.t -> 'a -> lval -> VD.t -> (unit -> 'a -> doc) -> exp -> D.t val id_meet_down: old:ID.t -> c:ID.t -> ID.t @@ -44,6 +48,36 @@ struct | `Bot -> false (* HACK: bot is here due to typing conflict (we do not cast appropriately) *) | _ -> VD.is_bot_value x + let apply_invariant oldv newv = + match oldv, newv with + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o && AD.mem (Addr.unknown_ptr ()) n -> *) + (* `Address (AD.join o n) *) + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) o -> `Address n *) + (* | `Address o, `Address n when AD.mem (Addr.unknown_ptr ()) n -> `Address o *) + | _ -> VD.meet oldv newv + + let refine_lv_fallback ctx a gs st lval value tv = + if M.tracing then M.tracec "invariant" "Restricting %a with %a\n" d_lval lval VD.pretty value; + let addr = eval_lv a gs st lval in + if (AD.is_top addr) then st + else + let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) + let t_lval = Cilfacade.typeOfLval lval in + let oldval = map_oldval oldval t_lval in + let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in + let state_with_excluded = set a gs st addr t_lval value ~ctx in + let value = get a gs state_with_excluded addr None in + let new_val = apply_invariant oldval value in + if M.tracing then M.traceu "invariant" "New value is %a\n" VD.pretty new_val; + (* make that address meet the invariant, i.e exclusion sets will be joined *) + if is_some_bot new_val then ( + if M.tracing then M.tracel "branch" "C The branch %B is dead!\n" tv; + raise Analyses.Deadcode + ) + else if VD.is_bot new_val + then set a gs st addr t_lval value ~ctx (* no *_raw because this is not a real assignment *) + else set a gs st addr t_lval new_val ~ctx (* no *_raw because this is not a real assignment *) + let invariant_fallback ctx a (gs:V.t -> G.t) st exp tv = (* We use a recursive helper function so that x != 0 is false can be handled * as x == 0 is true etc *) From f114a1f8b3833652251d4957b387cc9ea539e586 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 8 Nov 2022 12:00:00 +0200 Subject: [PATCH 112/132] Extract eval_rv_base_lval in base --- src/analyses/base.ml | 164 ++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 105 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f9da5c8ae9..597b3903bb 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -780,57 +780,8 @@ struct `Address (AD.from_string x) (* `Address (AD.str_ptr ()) *) | Const _ -> VD.top () (* Variables and address expressions *) - | Lval (Var v, ofs) -> do_offs (get a gs st (eval_lv a gs st (Var v, ofs)) (Some exp)) ofs - (*| Lval (Mem e, ofs) -> do_offs (get a gs st (eval_lv a gs st (Mem e, ofs))) ofs*) - | Lval (Mem e, ofs) -> - (*M.tracel "cast" "Deref: lval: %a\n" d_plainlval lv;*) - let rec contains_vla (t:typ) = match t with - | TPtr (t, _) -> contains_vla t - | TArray(t, None, args) -> true - | TArray(t, Some exp, args) when isConstant exp -> contains_vla t - | TArray(t, Some exp, args) -> true - | _ -> false - in - let b = Mem e, NoOffset in (* base pointer *) - let t = Cilfacade.typeOfLval b in (* static type of base *) - let p = eval_lv a gs st b in (* abstract base addresses *) - let v = (* abstract base value *) - let open Addr in - (* pre VLA: *) - (* let cast_ok = function Addr a -> sizeOf t <= sizeOf (get_type_addr a) | _ -> false in *) - let cast_ok = function - | Addr (x, o) -> - begin - let at = get_type_addr (x, o) in - if M.tracing then M.tracel "evalint" "cast_ok %a %a %a\n" Addr.pretty (Addr (x, o)) CilType.Typ.pretty (Cil.unrollType x.vtype) CilType.Typ.pretty at; - if at = TVoid [] then (* HACK: cast from alloc variable is always fine *) - true - else - match Cil.getInteger (sizeOf t), Cil.getInteger (sizeOf at) with - | Some i1, Some i2 -> Cilint.compare_cilint i1 i2 <= 0 - | _ -> - if contains_vla t || contains_vla (get_type_addr (x, o)) then - begin - (* TODO: Is this ok? *) - M.info ~category:Unsound "Casting involving a VLA is assumed to work"; - true - end - else - false - end - | NullPtr | UnknownPtr -> true (* TODO: are these sound? *) - | _ -> false - in - if AD.for_all cast_ok p then - get ~top:(VD.top_value t) a gs st p (Some exp) (* downcasts are safe *) - else - VD.top () (* upcasts not! *) - in - let v' = VD.cast t v in (* cast to the expected type (the abstract type might be something other than t since we don't change addresses upon casts!) *) - if M.tracing then M.tracel "cast" "Ptr-Deref: cast %a to %a = %a!\n" VD.pretty v d_type t VD.pretty v'; - let v' = VD.eval_offset a (fun x -> get a gs st x (Some exp)) v' (convert_offset a gs st ofs) (Some exp) None t in (* handle offset *) - let v' = do_offs v' ofs in (* handle blessed fields? *) - v' + | Lval lv -> + eval_rv_base_lval ~eval_lv ~do_offs a gs st exp lv (* Binary operators *) (* Eq/Ne when both values are equal and casted to the same type *) | BinOp ((Eq | Ne) as op, (CastE (t1, e1) as c1), (CastE (t2, e2) as c2), typ) when typeSig t1 = typeSig t2 -> @@ -952,6 +903,60 @@ struct if M.tracing then M.traceu "evalint" "base eval_rv_base %a -> %a\n" d_exp exp VD.pretty r; r + and eval_rv_base_lval ~eval_lv ~do_offs (a: Q.ask) (gs:glob_fun) (st: store) (exp: exp) (lv: lval): value = + match lv with + | (Var v, ofs) -> do_offs (get a gs st (eval_lv a gs st (Var v, ofs)) (Some exp)) ofs + (*| Lval (Mem e, ofs) -> do_offs (get a gs st (eval_lv a gs st (Mem e, ofs))) ofs*) + | (Mem e, ofs) -> + (*M.tracel "cast" "Deref: lval: %a\n" d_plainlval lv;*) + let rec contains_vla (t:typ) = match t with + | TPtr (t, _) -> contains_vla t + | TArray(t, None, args) -> true + | TArray(t, Some exp, args) when isConstant exp -> contains_vla t + | TArray(t, Some exp, args) -> true + | _ -> false + in + let b = Mem e, NoOffset in (* base pointer *) + let t = Cilfacade.typeOfLval b in (* static type of base *) + let p = eval_lv a gs st b in (* abstract base addresses *) + let v = (* abstract base value *) + let open Addr in + (* pre VLA: *) + (* let cast_ok = function Addr a -> sizeOf t <= sizeOf (get_type_addr a) | _ -> false in *) + let cast_ok = function + | Addr (x, o) -> + begin + let at = get_type_addr (x, o) in + if M.tracing then M.tracel "evalint" "cast_ok %a %a %a\n" Addr.pretty (Addr (x, o)) CilType.Typ.pretty (Cil.unrollType x.vtype) CilType.Typ.pretty at; + if at = TVoid [] then (* HACK: cast from alloc variable is always fine *) + true + else + match Cil.getInteger (sizeOf t), Cil.getInteger (sizeOf at) with + | Some i1, Some i2 -> Cilint.compare_cilint i1 i2 <= 0 + | _ -> + if contains_vla t || contains_vla (get_type_addr (x, o)) then + begin + (* TODO: Is this ok? *) + M.info ~category:Unsound "Casting involving a VLA is assumed to work"; + true + end + else + false + end + | NullPtr | UnknownPtr -> true (* TODO: are these sound? *) + | _ -> false + in + if AD.for_all cast_ok p then + get ~top:(VD.top_value t) a gs st p (Some exp) (* downcasts are safe *) + else + VD.top () (* upcasts not! *) + in + let v' = VD.cast t v in (* cast to the expected type (the abstract type might be something other than t since we don't change addresses upon casts!) *) + if M.tracing then M.tracel "cast" "Ptr-Deref: cast %a to %a = %a!\n" VD.pretty v d_type t VD.pretty v'; + let v' = VD.eval_offset a (fun x -> get a gs st x (Some exp)) v' (convert_offset a gs st ofs) (Some exp) None t in (* handle offset *) + let v' = do_offs v' ofs in (* handle blessed fields? *) + v' + and evalbinop (a: Q.ask) (gs:glob_fun) (st: store) (op: binop) ~(e1:exp) ?(t1:typ option) ~(e2:exp) ?(t2:typ option) (t:typ): value = evalbinop_mustbeequal a gs st op ~e1 ?t1 ~e2 ?t2 t @@ -2375,61 +2380,10 @@ struct (* old: *) (* eval_rv a gs st (Lval lv) *) - (* new, copied from eval_rv_base to use different ctx for eval_lv (for Mem): *) - (* TODO: deduplicate *) + (* new, use different ctx for eval_lv (for Mem): *) + let eval_lv a gs st lv = eval_lv oa gs ost lv in (* TODO: deduplicate *) let do_offs def o = def in (* HACK: no do_offs blessed here *) - match lv with - | (Var v, ofs) -> do_offs (get a gs st (eval_lv oa gs ost (Var v, ofs)) (Some exp)) ofs - (*| Lval (Mem e, ofs) -> do_offs (get a gs st (eval_lv a gs st (Mem e, ofs))) ofs*) - | (Mem e, ofs) -> - (*M.tracel "cast" "Deref: lval: %a\n" d_plainlval lv;*) - let rec contains_vla (t:typ) = match t with - | TPtr (t, _) -> contains_vla t - | TArray(t, None, args) -> true - | TArray(t, Some exp, args) when isConstant exp -> contains_vla t - | TArray(t, Some exp, args) -> true - | _ -> false - in - let b = Mem e, NoOffset in (* base pointer *) - let t = Cilfacade.typeOfLval b in (* static type of base *) - let p = eval_lv oa gs ost b in (* abstract base addresses *) - let v = (* abstract base value *) - let open Addr in - (* pre VLA: *) - (* let cast_ok = function Addr a -> sizeOf t <= sizeOf (get_type_addr a) | _ -> false in *) - let cast_ok = function - | Addr (x, o) -> - begin - let at = get_type_addr (x, o) in - if M.tracing then M.tracel "evalint" "cast_ok %a %a %a\n" Addr.pretty (Addr (x, o)) CilType.Typ.pretty (Cil.unrollType x.vtype) CilType.Typ.pretty at; - if at = TVoid [] then (* HACK: cast from alloc variable is always fine *) - true - else - match Cil.getInteger (sizeOf t), Cil.getInteger (sizeOf at) with - | Some i1, Some i2 -> Cilint.compare_cilint i1 i2 <= 0 - | _ -> - if contains_vla t || contains_vla (get_type_addr (x, o)) then - begin - (* TODO: Is this ok? *) - M.info ~category:Unsound "Casting involving a VLA is assumed to work"; - true - end - else - false - end - | NullPtr | UnknownPtr -> true (* TODO: are these sound? *) - | _ -> false - in - if AD.for_all cast_ok p then - get ~top:(VD.top_value t) a gs st p (Some exp) (* downcasts are safe *) - else - VD.top () (* upcasts not! *) - in - let v' = VD.cast t v in (* cast to the expected type (the abstract type might be something other than t since we don't change addresses upon casts!) *) - if M.tracing then M.tracel "cast" "Ptr-Deref: cast %a to %a = %a!\n" VD.pretty v d_type t VD.pretty v'; - let v' = VD.eval_offset a (fun x -> get a gs st x (Some exp)) v' (convert_offset a gs st ofs) (Some exp) None t in (* handle offset *) - let v' = do_offs v' ofs in (* handle blessed fields? *) - v' + eval_rv_base_lval ~eval_lv ~do_offs a gs st exp lv in let set' lval v st = set a gs st (eval_lv oa gs ost lval) (Cilfacade.typeOfLval lval) v ~invariant:false ~ctx in (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) match x with From 312b8b41d7e14201d1dc8b15b2acd8764905d7b1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 8 Nov 2022 12:34:23 +0200 Subject: [PATCH 113/132] Deduplicate BaseInvariant refine_lv --- src/analyses/base.ml | 85 ++++++----------------------------- src/analyses/baseInvariant.ml | 33 +++++++++++++- 2 files changed, 45 insertions(+), 73 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 597b3903bb..8b896abc5a 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1577,38 +1577,14 @@ struct let eval_rv = eval_rv let eval_rv_address = eval_rv_address let eval_lv = eval_lv + let convert_offset = convert_offset - let refine_lv ctx a gs st c x c' pretty exp = - let eval e st = eval_rv a gs st e in - let set' lval v st = set a gs st (eval_lv a gs st lval) (Cilfacade.typeOfLval lval) v ~invariant:true ~ctx in - match x with - | Var var, o -> - (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) - let oldv = get_var a gs st var in - let offs = convert_offset a gs st o in - let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in - let v = VD.meet oldv newv in - if is_some_bot v then raise Deadcode - else ( - if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v pretty c VD.pretty c'; - let r = set' (Var var,NoOffset) v st in - if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; - r - ) - | Mem _, _ -> - (* For accesses via pointers, not yet *) - let oldv = eval (Lval x) st in - let v = VD.meet oldv c' in - if is_some_bot v then raise Deadcode - else ( - if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; - set' x v st - ) - + let get_var = get_var let get a gs st addrs exp = get a gs st addrs exp let set a ~ctx gs st lval lval_type value = set a ~ctx ~invariant:true gs st lval lval_type value let map_oldval oldval _ = oldval + let eval_rv_lval_refine a gs st exp lval = eval_rv a gs st (Lval lval) let id_meet_down ~old ~c = ID.meet old c let fd_meet_down ~old ~c = FD.meet old c @@ -2373,56 +2349,23 @@ struct let oa = Analyses.ask_of_ctx octx let ost = octx.local - (* all updates happen in ctx with top values *) - - let refine_lv ctx a gs st c x c' pretty exp = - let eval_rv_lval lv st = - (* old: *) - (* eval_rv a gs st (Lval lv) *) - - (* new, use different ctx for eval_lv (for Mem): *) - let eval_lv a gs st lv = eval_lv oa gs ost lv in (* TODO: deduplicate *) - let do_offs def o = def in (* HACK: no do_offs blessed here *) - eval_rv_base_lval ~eval_lv ~do_offs a gs st exp lv - in - let set' lval v st = set a gs st (eval_lv oa gs ost lval) (Cilfacade.typeOfLval lval) v ~invariant:false ~ctx in (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) - match x with - | Var var, o -> - (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) - let oldv = get_var a gs st var in - let oldv = if VD.is_bot oldv then VD.top_value var.vtype else oldv in - let offs = convert_offset oa gs ost o in - let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in - let v = VD.meet oldv newv in - if is_some_bot v then raise Deadcode - else ( - if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v pretty c VD.pretty c'; - let r = set' (Var var,NoOffset) v st in - if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; - r - ) - | Mem _, _ -> - (* For accesses via pointers, not yet *) - let oldv = eval_rv_lval x st in - let oldv = if VD.is_bot oldv then VD.top_value (Cilfacade.typeOfLval x) else oldv in - let v = VD.meet oldv c' in - if is_some_bot v then raise Deadcode - else ( - if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; - set' x v st - ) - (* all evals happen in octx with non-top values *) - (* must be down here to make evals in refines call the real ones *) + let eval_rv a gs st e = eval_rv oa gs ost e + let eval_rv_address a gs st e = eval_rv_address oa gs ost e + let eval_lv a gs st lv = eval_lv oa gs ost lv + let convert_offset a gs st o = convert_offset oa gs ost o + + (* all updates happen in ctx with top values *) + let get_var = get_var let get a gs st addrs exp = get a gs st addrs exp let set a ~ctx gs st lval lval_type value = set a ~ctx ~invariant:false gs st lval lval_type value (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) let map_oldval oldval t_lval = if VD.is_bot oldval then VD.top_value t_lval else oldval - - let eval_rv a gs st e = eval_rv oa gs ost e - let eval_rv_address a gs st e = eval_rv_address oa gs ost e - let eval_lv a gs st lv = eval_lv oa gs ost lv + let eval_rv_lval_refine a gs st exp lv = + (* new, use different ctx for eval_lv (for Mem): *) + let do_offs def o = def in (* HACK: no do_offs blessed here *) + eval_rv_base_lval ~eval_lv ~do_offs a gs st exp lv (* don't meet with current octx values when propagating inverse operands down *) let id_meet_down ~old ~c = c diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index dd41c7bea8..6e9a7805e8 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -17,13 +17,14 @@ sig val eval_rv: Queries.ask -> (V.t -> G.t) -> D.t -> exp -> VD.t val eval_rv_address: Queries.ask -> (V.t -> G.t) -> D.t -> exp -> VD.t val eval_lv: Queries.ask -> (V.t -> G.t) -> D.t -> lval -> AD.t + val convert_offset: Queries.ask -> (V.t -> G.t) -> D.t -> offset -> (fieldinfo, ID.t) Lval.offs + val get_var: Queries.ask -> (V.t -> G.t) -> D.t -> varinfo -> VD.t val get: Queries.ask -> (V.t -> G.t) -> D.t -> AD.t -> exp option -> VD.t val set: Queries.ask -> ctx:(D.t, G.t, _, V.t) Analyses.ctx -> (V.t -> G.t) -> D.t -> AD.t -> typ -> VD.t -> D.t val map_oldval: VD.t -> typ -> VD.t - - val refine_lv: (D.t, G.t, _, V.t) Analyses.ctx -> Queries.ask -> (V.t -> G.t) -> D.t -> 'a -> lval -> VD.t -> (unit -> 'a -> doc) -> exp -> D.t + val eval_rv_lval_refine: Queries.ask -> (V.t -> G.t) -> D.t -> exp -> lval -> VD.t val id_meet_down: old:ID.t -> c:ID.t -> ID.t val fd_meet_down: old:FD.t -> c:FD.t -> FD.t @@ -78,6 +79,34 @@ struct then set a gs st addr t_lval value ~ctx (* no *_raw because this is not a real assignment *) else set a gs st addr t_lval new_val ~ctx (* no *_raw because this is not a real assignment *) + let refine_lv ctx a gs st c x c' pretty exp = + let set' lval v st = set a gs st (eval_lv a gs st lval) (Cilfacade.typeOfLval lval) v ~ctx in + match x with + | Var var, o -> + (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) + let oldv = get_var a gs st var in + let oldv = map_oldval oldv var.vtype in + let offs = convert_offset a gs st o in + let newv = VD.update_offset a oldv offs c' (Some exp) x (var.vtype) in + let v = VD.meet oldv newv in + if is_some_bot v then raise Analyses.Deadcode + else ( + if M.tracing then M.tracel "inv" "improve variable %a from %a to %a (c = %a, c' = %a)\n" d_varinfo var VD.pretty oldv VD.pretty v pretty c VD.pretty c'; + let r = set' (Var var,NoOffset) v st in + if M.tracing then M.tracel "inv" "st from %a to %a\n" D.pretty st D.pretty r; + r + ) + | Mem _, _ -> + (* For accesses via pointers, not yet *) + let oldv = eval_rv_lval_refine a gs st exp x in + let oldv = map_oldval oldv (Cilfacade.typeOfLval x) in + let v = VD.meet oldv c' in + if is_some_bot v then raise Analyses.Deadcode + else ( + if M.tracing then M.tracel "inv" "improve lval %a from %a to %a (c = %a, c' = %a)\n" d_lval x VD.pretty oldv VD.pretty v pretty c VD.pretty c'; + set' x v st + ) + let invariant_fallback ctx a (gs:V.t -> G.t) st exp tv = (* We use a recursive helper function so that x != 0 is false can be handled * as x == 0 is true etc *) From 8915b0148582135275d57f54c5b471c3d823a705 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 8 Nov 2022 12:38:12 +0200 Subject: [PATCH 114/132] Fix unconditional tracing in BaseInvariant --- src/analyses/baseInvariant.ml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 6e9a7805e8..54cc70564e 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -65,7 +65,14 @@ struct let oldval = get a gs st addr None in (* None is ok here, we could try to get more precise, but this is ok (reading at unknown position in array) *) let t_lval = Cilfacade.typeOfLval lval in let oldval = map_oldval oldval t_lval in - let oldval = if is_some_bot oldval then (M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; VD.top ()) else oldval in + let oldval = + if is_some_bot oldval then ( + if M.tracing then M.tracec "invariant" "%a is bot! This should not happen. Will continue with top!" d_lval lval; + VD.top () + ) + else + oldval + in let state_with_excluded = set a gs st addr t_lval value ~ctx in let value = get a gs state_with_excluded addr None in let new_val = apply_invariant oldval value in From b1e0851e9dc63522cb27922f4329260b10cd4be7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 8 Nov 2022 12:57:44 +0200 Subject: [PATCH 115/132] Unskip 56-witness/14-base-unassume-precondition --- tests/regression/56-witness/14-base-unassume-precondition.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/56-witness/14-base-unassume-precondition.c b/tests/regression/56-witness/14-base-unassume-precondition.c index 2aece436d3..8e6a1b3c73 100644 --- a/tests/regression/56-witness/14-base-unassume-precondition.c +++ b/tests/regression/56-witness/14-base-unassume-precondition.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 14-base-unassume-precondition.yml +// PARAM: --enable ana.int.interval --set ana.activated[+] unassume --set witness.yaml.unassume 14-base-unassume-precondition.yml --set witness.yaml.entry-types[+] precondition_loop_invariant #include void foo(int n) { From 799cbd5fd7e87c5d47731744a1907ec0827d5b61 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 17 Nov 2022 17:16:31 +0200 Subject: [PATCH 116/132] Support location_invariant in unassume --- src/analyses/unassumeAnalysis.ml | 22 +++++++++++++++++-- .../56-witness/10-apron-unassume-interval.yml | 8 +++---- .../56-witness/11-base-unassume-interval.yml | 8 +++---- .../56-witness/12-apron-unassume-branch.yml | 8 +++---- .../56-witness/13-base-unassume-branch.yml | 8 +++---- .../14-base-unassume-precondition.yml | 4 ++-- .../56-witness/15-base-unassume-query.yml | 8 +++---- .../56-witness/16-base-unassume-dependent.yml | 12 +++++----- .../56-witness/17-base-unassume-tauto.yml | 4 ++-- .../56-witness/18-base-unassume-contra.yml | 4 ++-- .../56-witness/19-base-unassume-mem.yml | 8 +++---- .../56-witness/20-apron-unassume-global.yml | 8 +++---- .../56-witness/21-apron-unassume-priv.yml | 4 ++-- .../56-witness/22-base-unassume-priv.yml | 4 ++-- .../56-witness/23-base-unassume-priv2.yml | 4 ++-- .../56-witness/24-apron-unassume-priv2.yml | 4 ++-- .../25-apron-unassume-strengthening.yml | 4 ++-- .../56-witness/26-mine-tutorial-ex4.6.yml | 4 ++-- .../56-witness/27-mine-tutorial-ex4.7.yml | 4 ++-- .../56-witness/28-mine-tutorial-ex4.8.yml | 4 ++-- .../56-witness/29-mine-tutorial-ex4.10.yml | 4 ++-- .../56-witness/30-base-unassume-inc-dec.yml | 8 +++---- .../56-witness/31-base-unassume-mem-ex.yml | 12 +++++----- .../56-witness/32-base-unassume-lor-addr.yml | 4 ++-- .../56-witness/33-base-unassume-lor-enums.yml | 4 ++-- .../34-base-unassume-inc-dec-traces.yml | 8 +++---- tests/regression/56-witness/35-hh-ex1b.yml | 4 ++-- tests/regression/56-witness/36-hh-ex2b.yml | 4 ++-- tests/regression/56-witness/37-hh-ex3.yml | 4 ++-- tests/regression/56-witness/38-bh-ex3.yml | 4 ++-- tests/regression/56-witness/39-bh-ex-add.yml | 4 ++-- .../regression/56-witness/40-bh-ex1-poly.yml | 4 ++-- tests/regression/56-witness/41-as-hybrid.yml | 4 ++-- 33 files changed, 110 insertions(+), 92 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index db7896b5d2..d9dc03743f 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -25,6 +25,7 @@ struct module Locator = WitnessUtil.Locator (Node) let locator: Locator.t ref = ref (Locator.create ()) (* empty default, so don't have to use option everywhere *) + let loop_locator: Locator.t ref = ref (Locator.create ()) (* empty default, so don't have to use option everywhere *) type inv = { exp: Cil.exp; @@ -38,6 +39,7 @@ struct let init _ = locator := Locator.create (); (* TODO: add Locator.clear *) + loop_locator := Locator.create (); (* TODO: add Locator.clear *) let module Cfg = (val !MyCFG.current_cfg) in let module WitnessInvariant = WitnessUtil.Invariant (struct let file = !Cilfacade.current_file end) (Cfg) in @@ -49,6 +51,8 @@ struct (* TODO: filter synthetic like in Validator *) if WitnessInvariant.is_invariant_node node then Locator.add !locator (Node.location node) node; + if WitnessUtil.NH.mem WitnessInvariant.loop_heads node then + Locator.add !loop_locator (Node.location node) node; List.iter (fun (_, prev_node) -> iter_node prev_node ) (Cfg.prev node) @@ -110,12 +114,24 @@ struct M.info ~category:Witness ~loc:msgLoc "invariant has invalid syntax: %s" inv in + let unassume_location_invariant (location_invariant: YamlWitnessType.LocationInvariant.t) = + let loc = loc_of_location location_invariant.location in + let inv = location_invariant.location_invariant.string in + let msgLoc: M.Location.t = CilLocation loc in + + match Locator.find_opt !locator loc with + | Some nodes -> + unassume_nodes_invariant ~loc ~nodes inv + | None -> + M.warn ~category:Witness ~loc:msgLoc "couldn't locate invariant: %s" inv + in + let unassume_loop_invariant (loop_invariant: YamlWitnessType.LoopInvariant.t) = let loc = loc_of_location loop_invariant.location in let inv = loop_invariant.loop_invariant.string in let msgLoc: M.Location.t = CilLocation loc in - match Locator.find_opt !locator loc with + match Locator.find_opt !loop_locator loc with | Some nodes -> unassume_nodes_invariant ~loc ~nodes inv | None -> @@ -174,11 +190,13 @@ struct in match YamlWitness.entry_type_enabled target_type, entry.entry_type with + | true, LocationInvariant x -> + unassume_location_invariant x | true, LoopInvariant x -> unassume_loop_invariant x | true, PreconditionLoopInvariant x -> unassume_precondition_loop_invariant x - | false, (LoopInvariant _ | PreconditionLoopInvariant _) -> + | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _) -> M.info_noloc ~category:Witness "disabled entry of type %s" target_type | _ -> M.info_noloc ~category:Witness "cannot unassume entry of type %s" target_type diff --git a/tests/regression/56-witness/10-apron-unassume-interval.yml b/tests/regression/56-witness/10-apron-unassume-interval.yml index f7958922ae..12cc344957 100644 --- a/tests/regression/56-witness/10-apron-unassume-interval.yml +++ b/tests/regression/56-witness/10-apron-unassume-interval.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 0a72f7b3-7826-4f68-bc7b-25425e95946e @@ -22,11 +22,11 @@ line: 6 column: 2 function: main - loop_invariant: + location_invariant: string: 100LL - (long long )i >= 0LL type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 4e078bcd-9e55-4874-a86e-0563927704a5 @@ -50,7 +50,7 @@ line: 6 column: 2 function: main - loop_invariant: + location_invariant: string: (long long )i >= 0LL type: assertion format: C diff --git a/tests/regression/56-witness/11-base-unassume-interval.yml b/tests/regression/56-witness/11-base-unassume-interval.yml index df939b7ca4..bb4740c7b0 100644 --- a/tests/regression/56-witness/11-base-unassume-interval.yml +++ b/tests/regression/56-witness/11-base-unassume-interval.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 0a72f7b3-7826-4f68-bc7b-25425e95946e @@ -22,11 +22,11 @@ line: 6 column: 2 function: main - loop_invariant: + location_invariant: string: 100LL - (long long )i >= 0LL type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 4e078bcd-9e55-4874-a86e-0563927704a5 @@ -50,7 +50,7 @@ line: 6 column: 2 function: main - loop_invariant: + location_invariant: string: (long long )i >= 0LL type: assertion format: C diff --git a/tests/regression/56-witness/12-apron-unassume-branch.yml b/tests/regression/56-witness/12-apron-unassume-branch.yml index 2381291880..0ea4445131 100644 --- a/tests/regression/56-witness/12-apron-unassume-branch.yml +++ b/tests/regression/56-witness/12-apron-unassume-branch.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 0a72f7b3-7826-4f68-bc7b-25425e95946e @@ -22,11 +22,11 @@ line: 7 column: 4 function: main - loop_invariant: + location_invariant: string: 99LL - (long long )i >= 0LL type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 4e078bcd-9e55-4874-a86e-0563927704a5 @@ -50,7 +50,7 @@ line: 7 column: 4 function: main - loop_invariant: + location_invariant: string: (long long )i >= 0LL type: assertion format: C diff --git a/tests/regression/56-witness/13-base-unassume-branch.yml b/tests/regression/56-witness/13-base-unassume-branch.yml index 22253938b7..f9186f626d 100644 --- a/tests/regression/56-witness/13-base-unassume-branch.yml +++ b/tests/regression/56-witness/13-base-unassume-branch.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 0a72f7b3-7826-4f68-bc7b-25425e95946e @@ -22,11 +22,11 @@ line: 7 column: 4 function: main - loop_invariant: + location_invariant: string: 99LL - (long long )i >= 0LL type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 4e078bcd-9e55-4874-a86e-0563927704a5 @@ -50,7 +50,7 @@ line: 7 column: 4 function: main - loop_invariant: + location_invariant: string: (long long )i >= 0LL type: assertion format: C diff --git a/tests/regression/56-witness/14-base-unassume-precondition.yml b/tests/regression/56-witness/14-base-unassume-precondition.yml index 2d84e4ea9f..df4bbc8dc7 100644 --- a/tests/regression/56-witness/14-base-unassume-precondition.yml +++ b/tests/regression/56-witness/14-base-unassume-precondition.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 26e6d2de-13a6-4610-9b08-ee3d9e6b9338 @@ -21,7 +21,7 @@ line: 6 column: 2 function: foo - loop_invariant: + location_invariant: string: 0 <= i type: assertion format: C diff --git a/tests/regression/56-witness/15-base-unassume-query.yml b/tests/regression/56-witness/15-base-unassume-query.yml index 46b0ffc4c5..cf037d2d62 100644 --- a/tests/regression/56-witness/15-base-unassume-query.yml +++ b/tests/regression/56-witness/15-base-unassume-query.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: dc8526c7-69d5-4ec2-b90c-670bd19c6f1a @@ -20,11 +20,11 @@ line: 16 column: 2 function: main - loop_invariant: + location_invariant: string: i == 2 type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 48ab49c6-c030-446d-9b31-673979f1cbd0 @@ -46,7 +46,7 @@ line: 16 column: 2 function: main - loop_invariant: + location_invariant: string: j == 3 type: assertion format: C diff --git a/tests/regression/56-witness/16-base-unassume-dependent.yml b/tests/regression/56-witness/16-base-unassume-dependent.yml index 78c4ce4490..fe8dcd303b 100644 --- a/tests/regression/56-witness/16-base-unassume-dependent.yml +++ b/tests/regression/56-witness/16-base-unassume-dependent.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 349896cf-62cc-42e7-b9a3-f5f422ff4071 @@ -22,11 +22,11 @@ line: 8 column: 2 function: main - loop_invariant: + location_invariant: string: 0 <= i type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 349896cf-62cc-42e7-b9a3-f5f422ff4071 @@ -50,11 +50,11 @@ line: 8 column: 2 function: main - loop_invariant: + location_invariant: string: i <= j type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 349896cf-62cc-42e7-b9a3-f5f422ff4071 @@ -78,7 +78,7 @@ line: 8 column: 2 function: main - loop_invariant: + location_invariant: string: j <= 42 type: assertion format: C diff --git a/tests/regression/56-witness/17-base-unassume-tauto.yml b/tests/regression/56-witness/17-base-unassume-tauto.yml index 17843875fb..358434238c 100644 --- a/tests/regression/56-witness/17-base-unassume-tauto.yml +++ b/tests/regression/56-witness/17-base-unassume-tauto.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 828f8b0f-590b-4180-bef1-d2b18cc240e6 @@ -22,7 +22,7 @@ line: 7 column: 2 function: main - loop_invariant: + location_invariant: string: i <= i + 1 type: assertion format: C diff --git a/tests/regression/56-witness/18-base-unassume-contra.yml b/tests/regression/56-witness/18-base-unassume-contra.yml index cd618e7ae4..c3306e1ffa 100644 --- a/tests/regression/56-witness/18-base-unassume-contra.yml +++ b/tests/regression/56-witness/18-base-unassume-contra.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: e9cde5db-0c69-4413-ad46-7a15b93f1834 @@ -22,7 +22,7 @@ line: 7 column: 2 function: main - loop_invariant: + location_invariant: string: i + 1 <= i type: assertion format: C diff --git a/tests/regression/56-witness/19-base-unassume-mem.yml b/tests/regression/56-witness/19-base-unassume-mem.yml index 37c47d57e4..9d68446eba 100644 --- a/tests/regression/56-witness/19-base-unassume-mem.yml +++ b/tests/regression/56-witness/19-base-unassume-mem.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: ddb9942d-998c-425d-a74f-228044bc38fb @@ -21,11 +21,11 @@ line: 14 column: 2 function: main - loop_invariant: + location_invariant: string: '*p >= 0' type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: ddb9942d-998c-425d-a74f-228044bc38fb @@ -48,7 +48,7 @@ line: 14 column: 2 function: main - loop_invariant: + location_invariant: string: '*p <= 10' type: assertion format: C diff --git a/tests/regression/56-witness/20-apron-unassume-global.yml b/tests/regression/56-witness/20-apron-unassume-global.yml index 6f824d4f9f..641adcac2b 100644 --- a/tests/regression/56-witness/20-apron-unassume-global.yml +++ b/tests/regression/56-witness/20-apron-unassume-global.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 0a72f7b3-7826-4f68-bc7b-25425e95946e @@ -22,11 +22,11 @@ line: 6 column: 2 function: main - loop_invariant: + location_invariant: string: 100LL - (long long )i >= 0LL type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 4e078bcd-9e55-4874-a86e-0563927704a5 @@ -50,7 +50,7 @@ line: 6 column: 2 function: main - loop_invariant: + location_invariant: string: (long long )i >= 0LL type: assertion format: C diff --git a/tests/regression/56-witness/21-apron-unassume-priv.yml b/tests/regression/56-witness/21-apron-unassume-priv.yml index 257d03e357..ab3eb0c6c1 100644 --- a/tests/regression/56-witness/21-apron-unassume-priv.yml +++ b/tests/regression/56-witness/21-apron-unassume-priv.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 85b0932e-717e-47ea-b7a0-6ed5ca294047 @@ -23,7 +23,7 @@ line: 12 column: 2 function: t_fun - loop_invariant: + location_invariant: string: '0 <= g && g <= 10' type: assertion format: C diff --git a/tests/regression/56-witness/22-base-unassume-priv.yml b/tests/regression/56-witness/22-base-unassume-priv.yml index 3ebeca0b4e..f063ed7e6b 100644 --- a/tests/regression/56-witness/22-base-unassume-priv.yml +++ b/tests/regression/56-witness/22-base-unassume-priv.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 85b0932e-717e-47ea-b7a0-6ed5ca294047 @@ -23,7 +23,7 @@ line: 12 column: 2 function: t_fun - loop_invariant: + location_invariant: string: '0 <= g && g <= 10' type: assertion format: C diff --git a/tests/regression/56-witness/23-base-unassume-priv2.yml b/tests/regression/56-witness/23-base-unassume-priv2.yml index 7500fb87dd..4ca5142b0a 100644 --- a/tests/regression/56-witness/23-base-unassume-priv2.yml +++ b/tests/regression/56-witness/23-base-unassume-priv2.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 85b0932e-717e-47ea-b7a0-6ed5ca294047 @@ -23,7 +23,7 @@ line: 11 column: 2 function: t_fun - loop_invariant: + location_invariant: string: '0 <= g && g <= 10' type: assertion format: C diff --git a/tests/regression/56-witness/24-apron-unassume-priv2.yml b/tests/regression/56-witness/24-apron-unassume-priv2.yml index 01abee3ee8..d775d14929 100644 --- a/tests/regression/56-witness/24-apron-unassume-priv2.yml +++ b/tests/regression/56-witness/24-apron-unassume-priv2.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 85b0932e-717e-47ea-b7a0-6ed5ca294047 @@ -23,7 +23,7 @@ line: 11 column: 2 function: t_fun - loop_invariant: + location_invariant: string: '0 <= g && g <= 10' type: assertion format: C diff --git a/tests/regression/56-witness/25-apron-unassume-strengthening.yml b/tests/regression/56-witness/25-apron-unassume-strengthening.yml index 86bdd3aa99..75d4df8026 100644 --- a/tests/regression/56-witness/25-apron-unassume-strengthening.yml +++ b/tests/regression/56-witness/25-apron-unassume-strengthening.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: dec4db01-03ed-47dd-ae55-c84f8dcd51ed @@ -23,7 +23,7 @@ line: 8 column: 4 function: main - loop_invariant: + location_invariant: string: x >= 0 type: assertion format: C diff --git a/tests/regression/56-witness/26-mine-tutorial-ex4.6.yml b/tests/regression/56-witness/26-mine-tutorial-ex4.6.yml index 659537ba77..2e3c6758fc 100644 --- a/tests/regression/56-witness/26-mine-tutorial-ex4.6.yml +++ b/tests/regression/56-witness/26-mine-tutorial-ex4.6.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 0e84a9de-b9f6-44dd-ab8d-ebdeca941482 @@ -21,7 +21,7 @@ line: 5 column: 2 function: main - loop_invariant: + location_invariant: string: 0 <= x && x <= 40 type: assertion format: C diff --git a/tests/regression/56-witness/27-mine-tutorial-ex4.7.yml b/tests/regression/56-witness/27-mine-tutorial-ex4.7.yml index 8c94ac8e20..ad4c287227 100644 --- a/tests/regression/56-witness/27-mine-tutorial-ex4.7.yml +++ b/tests/regression/56-witness/27-mine-tutorial-ex4.7.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: e17f9860-95af-4213-a767-ab4876ead27e @@ -23,7 +23,7 @@ line: 6 column: 8 function: main - loop_invariant: + location_invariant: string: 0 <= x && x <= 40 type: assertion format: C diff --git a/tests/regression/56-witness/28-mine-tutorial-ex4.8.yml b/tests/regression/56-witness/28-mine-tutorial-ex4.8.yml index c92f0223e4..69479e6df0 100644 --- a/tests/regression/56-witness/28-mine-tutorial-ex4.8.yml +++ b/tests/regression/56-witness/28-mine-tutorial-ex4.8.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 299c2080-fb13-4879-be2b-5d2758465577 @@ -23,7 +23,7 @@ line: 6 column: 2 function: main - loop_invariant: + location_invariant: string: 0 <= v && v <= 1 type: assertion format: C diff --git a/tests/regression/56-witness/29-mine-tutorial-ex4.10.yml b/tests/regression/56-witness/29-mine-tutorial-ex4.10.yml index c719c76c0a..0f9b362290 100644 --- a/tests/regression/56-witness/29-mine-tutorial-ex4.10.yml +++ b/tests/regression/56-witness/29-mine-tutorial-ex4.10.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: f04fe372-3d6e-4a80-9567-80c64bd7fd03 @@ -24,7 +24,7 @@ line: 6 column: 2 function: main - loop_invariant: + location_invariant: string: 1 <= v && v <= 52 type: assertion format: C diff --git a/tests/regression/56-witness/30-base-unassume-inc-dec.yml b/tests/regression/56-witness/30-base-unassume-inc-dec.yml index a0c6de4a33..d328abe556 100644 --- a/tests/regression/56-witness/30-base-unassume-inc-dec.yml +++ b/tests/regression/56-witness/30-base-unassume-inc-dec.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: e1da1c44-5406-4bce-ab4a-3597a2b36e24 @@ -23,11 +23,11 @@ line: 10 column: 4 function: t_fun - loop_invariant: + location_invariant: string: -10 <= g && g <= 10 type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 16a70fb8-6f51-4a2c-a2e0-a68a4baf1575 @@ -52,7 +52,7 @@ line: 20 column: 4 function: t_fun2 - loop_invariant: + location_invariant: string: -10 <= g && g <= 10 type: assertion format: C diff --git a/tests/regression/56-witness/31-base-unassume-mem-ex.yml b/tests/regression/56-witness/31-base-unassume-mem-ex.yml index 1a1c4e378c..75b1dba18d 100644 --- a/tests/regression/56-witness/31-base-unassume-mem-ex.yml +++ b/tests/regression/56-witness/31-base-unassume-mem-ex.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 8a156304-0058-4a15-a934-884483183ba8 @@ -23,11 +23,11 @@ line: 16 column: 6 function: main - loop_invariant: + location_invariant: string: i >= 0 && j >= 0 type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 1d367e5b-84a7-42e3-8191-11384aeca509 @@ -52,11 +52,11 @@ line: 23 column: 6 function: main - loop_invariant: + location_invariant: string: (p == &i || p == &j) && *p >= 0 type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: a5dc6e03-3748-4e52-befc-8c846634e329 @@ -81,7 +81,7 @@ line: 30 column: 6 function: main - loop_invariant: + location_invariant: string: '*p >= 0' type: assertion format: C diff --git a/tests/regression/56-witness/32-base-unassume-lor-addr.yml b/tests/regression/56-witness/32-base-unassume-lor-addr.yml index da178f8d1a..a0c6476fe1 100644 --- a/tests/regression/56-witness/32-base-unassume-lor-addr.yml +++ b/tests/regression/56-witness/32-base-unassume-lor-addr.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: d2a65147-2d04-49f8-8f27-fe3a7c8553b7 @@ -22,7 +22,7 @@ line: 8 column: 2 function: main - loop_invariant: + location_invariant: string: p == &i || p == &j type: assertion format: C diff --git a/tests/regression/56-witness/33-base-unassume-lor-enums.yml b/tests/regression/56-witness/33-base-unassume-lor-enums.yml index e98d25602f..c3220fca14 100644 --- a/tests/regression/56-witness/33-base-unassume-lor-enums.yml +++ b/tests/regression/56-witness/33-base-unassume-lor-enums.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 8c07adbd-908d-45be-af99-943634ad66b4 @@ -23,7 +23,7 @@ line: 7 column: 2 function: main - loop_invariant: + location_invariant: string: i == 0 || i == 1 || i == 2 type: assertion format: C diff --git a/tests/regression/56-witness/34-base-unassume-inc-dec-traces.yml b/tests/regression/56-witness/34-base-unassume-inc-dec-traces.yml index e159e57c44..55fa88bd52 100644 --- a/tests/regression/56-witness/34-base-unassume-inc-dec-traces.yml +++ b/tests/regression/56-witness/34-base-unassume-inc-dec-traces.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: e1da1c44-5406-4bce-ab4a-3597a2b36e24 @@ -23,11 +23,11 @@ line: 10 column: 4 function: t_fun - loop_invariant: + location_invariant: string: -10 <= g && g <= 10 type: assertion format: C -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 16a70fb8-6f51-4a2c-a2e0-a68a4baf1575 @@ -52,7 +52,7 @@ line: 20 column: 4 function: t_fun2 - loop_invariant: + location_invariant: string: -10 <= g && g <= 10 type: assertion format: C diff --git a/tests/regression/56-witness/35-hh-ex1b.yml b/tests/regression/56-witness/35-hh-ex1b.yml index 0e2db4aeac..9763960fd7 100644 --- a/tests/regression/56-witness/35-hh-ex1b.yml +++ b/tests/regression/56-witness/35-hh-ex1b.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 1a354f1e-76e8-40e9-808a-8d5efbe35496 @@ -23,7 +23,7 @@ line: 7 column: 2 function: main - loop_invariant: + location_invariant: string: 0 <= i && i <= 99 type: assertion format: C diff --git a/tests/regression/56-witness/36-hh-ex2b.yml b/tests/regression/56-witness/36-hh-ex2b.yml index d2a7218a28..69e0a88841 100644 --- a/tests/regression/56-witness/36-hh-ex2b.yml +++ b/tests/regression/56-witness/36-hh-ex2b.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 9589b9d4-edce-4043-8413-279703946762 @@ -23,7 +23,7 @@ line: 6 column: 2 function: main - loop_invariant: + location_invariant: string: 0 <= n && n <= 60 type: assertion format: C diff --git a/tests/regression/56-witness/37-hh-ex3.yml b/tests/regression/56-witness/37-hh-ex3.yml index 77ec224d97..9a4562d6d2 100644 --- a/tests/regression/56-witness/37-hh-ex3.yml +++ b/tests/regression/56-witness/37-hh-ex3.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: d834761a-d0d7-4fea-bf42-2ff2b9a19143 @@ -23,7 +23,7 @@ line: 7 column: 4 function: main - loop_invariant: + location_invariant: string: 0 <= i && i <= 3 && j == 0 type: assertion format: C diff --git a/tests/regression/56-witness/38-bh-ex3.yml b/tests/regression/56-witness/38-bh-ex3.yml index 2e489e1f17..d1f6677fbb 100644 --- a/tests/regression/56-witness/38-bh-ex3.yml +++ b/tests/regression/56-witness/38-bh-ex3.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: bd436e09-244f-45db-a5f4-9337ca997424 @@ -23,7 +23,7 @@ line: 7 column: 2 function: main - loop_invariant: + location_invariant: string: 0 <= m && m <= 60 && 0 <= n && n <= 60 type: assertion format: C diff --git a/tests/regression/56-witness/39-bh-ex-add.yml b/tests/regression/56-witness/39-bh-ex-add.yml index 29d3006276..84aa374c1e 100644 --- a/tests/regression/56-witness/39-bh-ex-add.yml +++ b/tests/regression/56-witness/39-bh-ex-add.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: bd436e09-244f-45db-a5f4-9337ca997424 @@ -23,7 +23,7 @@ line: 7 column: 2 function: main - loop_invariant: + location_invariant: string: 0 <= m && m <= 60 && 0 <= n && n <= 60 type: assertion format: C diff --git a/tests/regression/56-witness/40-bh-ex1-poly.yml b/tests/regression/56-witness/40-bh-ex1-poly.yml index a5576d40ea..e219e1f877 100644 --- a/tests/regression/56-witness/40-bh-ex1-poly.yml +++ b/tests/regression/56-witness/40-bh-ex1-poly.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 6ee08942-60e6-4291-a9fc-ff6f2744ef4b @@ -23,7 +23,7 @@ line: 8 column: 4 function: main - loop_invariant: + location_invariant: string: 0 <= i && i <= 3 && j == 0 type: assertion format: C diff --git a/tests/regression/56-witness/41-as-hybrid.yml b/tests/regression/56-witness/41-as-hybrid.yml index 8eac9f75f7..0e810b6aca 100644 --- a/tests/regression/56-witness/41-as-hybrid.yml +++ b/tests/regression/56-witness/41-as-hybrid.yml @@ -1,4 +1,4 @@ -- entry_type: loop_invariant +- entry_type: location_invariant metadata: format_version: "0.1" uuid: 71b761c6-4f85-405e-8ee6-9a3a8f743513 @@ -23,7 +23,7 @@ line: 5 column: 2 function: main - loop_invariant: + location_invariant: string: 0 <= i && i <= 9 type: assertion format: C From 66b5b23b19e5dec8f0a59702a158672ae8651659 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Dec 2022 12:02:19 +0200 Subject: [PATCH 117/132] Add longer unassume analysis description --- src/analyses/unassumeAnalysis.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 44d19b7931..656fc2ae99 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -1,4 +1,6 @@ -(** Unassume analysis. *) +(** Unassume analysis. + + Emits unassume events for other analyses based on YAML witness invariants. *) open Analyses module Cil = GoblintCil.Cil From 1631657da36e2300d1ccb20bc2f97242844e76b1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Dec 2022 12:11:18 +0200 Subject: [PATCH 118/132] Add longer BenchZarith description --- bench/zarith/benchZarith.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bench/zarith/benchZarith.ml b/bench/zarith/benchZarith.ml index 7411b1e499..5830a226cf 100644 --- a/bench/zarith/benchZarith.ml +++ b/bench/zarith/benchZarith.ml @@ -1,4 +1,7 @@ -(* dune exec bench/zarith/benchZarith.exe -- -a *) +(** Benchmark calculation of powers of 2 with Zarith. + Originally for https://github.com/goblint/cil/pull/115. + + dune exec bench/zarith/benchZarith.exe -- -a *) open Benchmark open Benchmark.Tree From 419fcec51303ec57eea97474b2c10e5060007eb4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Dec 2022 12:20:03 +0200 Subject: [PATCH 119/132] Add WitnessUtil.Locator.clear --- src/analyses/unassumeAnalysis.ml | 18 +++++++++--------- src/witness/witnessUtil.ml | 3 +++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 656fc2ae99..ed79bc2dbc 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -26,8 +26,8 @@ struct module Locator = WitnessUtil.Locator (Node) - let locator: Locator.t ref = ref (Locator.create ()) (* empty default, so don't have to use option everywhere *) - let loop_locator: Locator.t ref = ref (Locator.create ()) (* empty default, so don't have to use option everywhere *) + let locator: Locator.t = Locator.create () (* empty default, so don't have to use option everywhere *) + let loop_locator: Locator.t = Locator.create () (* empty default, so don't have to use option everywhere *) type inv = { exp: Cil.exp; @@ -40,8 +40,8 @@ struct let pre_invs: inv EH.t NH.t = NH.create 100 let init _ = - locator := Locator.create (); (* TODO: add Locator.clear *) - loop_locator := Locator.create (); (* TODO: add Locator.clear *) + Locator.clear locator; + Locator.clear loop_locator; let module FileCfg = struct let file = !Cilfacade.current_file @@ -56,9 +56,9 @@ struct NH.replace reachable node (); (* TODO: filter synthetic like in Validator *) if WitnessInvariant.is_invariant_node node then - Locator.add !locator (Node.location node) node; + Locator.add locator (Node.location node) node; if WitnessUtil.NH.mem WitnessInvariant.loop_heads node then - Locator.add !loop_locator (Node.location node) node; + Locator.add loop_locator (Node.location node) node; List.iter (fun (_, prev_node) -> iter_node prev_node ) (FileCfg.Cfg.prev node) @@ -125,7 +125,7 @@ struct let inv = location_invariant.location_invariant.string in let msgLoc: M.Location.t = CilLocation loc in - match Locator.find_opt !locator loc with + match Locator.find_opt locator loc with | Some nodes -> unassume_nodes_invariant ~loc ~nodes inv | None -> @@ -137,7 +137,7 @@ struct let inv = loop_invariant.loop_invariant.string in let msgLoc: M.Location.t = CilLocation loc in - match Locator.find_opt !loop_locator loc with + match Locator.find_opt loop_locator loc with | Some nodes -> unassume_nodes_invariant ~loc ~nodes inv | None -> @@ -188,7 +188,7 @@ struct let inv = precondition_loop_invariant.loop_invariant.string in let msgLoc: M.Location.t = CilLocation loc in - match Locator.find_opt !locator loc with + match Locator.find_opt locator loc with | Some nodes -> unassume_precondition_nodes_invariant ~loc ~nodes pre inv | None -> diff --git a/src/witness/witnessUtil.ml b/src/witness/witnessUtil.ml index 620bd6b43a..0664e56584 100644 --- a/src/witness/witnessUtil.ml +++ b/src/witness/witnessUtil.ml @@ -190,4 +190,7 @@ struct None else Some es + + let clear (file_loc_es: t): unit = + FileH.clear file_loc_es end From 9109e44e3d7124dc24e74fb46c494fcff943ab4e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 19 Dec 2022 12:22:46 +0200 Subject: [PATCH 120/132] Improve LocalFixpoint description Co-authored-by: Michael Schwarz --- src/solvers/localFixpoint.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solvers/localFixpoint.ml b/src/solvers/localFixpoint.ml index 1c212520a7..63a6fab260 100644 --- a/src/solvers/localFixpoint.ml +++ b/src/solvers/localFixpoint.ml @@ -1,4 +1,4 @@ -(** Local fixpoint iteration solvers that don't use a constraint system. *) +(** Fixpoint iteration solvers local to a single transfer function (don't use a constraint system). *) module Make (D: Lattice.S) = struct From cedb27819f44b3bf720456dd73612b821e66cdad Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 22 Dec 2022 12:42:01 +0000 Subject: [PATCH 121/132] Update svcomp-yaml confs to svcomp conf, except autotune --- conf/svcomp-yaml-validate.json | 22 +++++++++++++++++----- conf/svcomp-yaml.json | 24 ++++++++++++++++++------ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/conf/svcomp-yaml-validate.json b/conf/svcomp-yaml-validate.json index f0a1e53fc4..ad635c787e 100644 --- a/conf/svcomp-yaml-validate.json +++ b/conf/svcomp-yaml-validate.json @@ -1,10 +1,5 @@ { "ana": { - "base": { - "arrays": { - "domain": "partitioned" - } - }, "sv-comp": { "enabled": true, "functions": true @@ -14,6 +9,9 @@ "enums": false, "interval": true }, + "float": { + "interval": true + }, "activated": [ "base", "threadid", @@ -23,13 +21,16 @@ "mutexEvents", "mutex", "access", + "race", "escape", "expRelation", "mhp", + "assert", "var_eq", "symb_locks", "region", "thread", + "threadJoins", "unassume" ], "context": { @@ -55,6 +56,14 @@ "ldv_calloc" ] }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "autotune": { + "enabled": false + }, "widen": { "tokens": true } @@ -77,6 +86,9 @@ }, "int": { "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" } } } diff --git a/conf/svcomp-yaml.json b/conf/svcomp-yaml.json index f9bc530a13..28483e8059 100644 --- a/conf/svcomp-yaml.json +++ b/conf/svcomp-yaml.json @@ -1,10 +1,5 @@ { "ana": { - "base": { - "arrays": { - "domain": "partitioned" - } - }, "sv-comp": { "enabled": true, "functions": true @@ -14,6 +9,9 @@ "enums": false, "interval": true }, + "float": { + "interval": true + }, "activated": [ "base", "threadid", @@ -23,13 +21,16 @@ "mutexEvents", "mutex", "access", + "race", "escape", "expRelation", "mhp", + "assert", "var_eq", "symb_locks", "region", - "thread" + "thread", + "threadJoins" ], "context": { "widen": false @@ -53,6 +54,14 @@ "ldv_xzalloc", "ldv_calloc" ] + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "autotune": { + "enabled": false } }, "exp": { @@ -84,6 +93,9 @@ }, "int": { "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" } } } From abc47e6b2753276b61916cd2110c1e132cbbb225 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 22 Dec 2022 13:28:41 +0000 Subject: [PATCH 122/132] Refactor YAML witness SV-COMP testing to use multiple run definitions --- .../yaml/goblint-validate.xml | 38 +++++++++------- sv-comp/my-bench-sv-comp/yaml/goblint.sh | 9 ++-- sv-comp/my-bench-sv-comp/yaml/goblint.xml | 34 +++++++++------ .../my-bench-sv-comp/yaml/table-generator.xml | 43 ++++++++++++++----- 4 files changed, 79 insertions(+), 45 deletions(-) diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml b/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml index ea578fa9e8..9a43316ee2 100644 --- a/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml +++ b/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml @@ -11,26 +11,32 @@ evals = (\d+) - + + + + - RESULTSDIR/LOGDIR/${rundefinition_name}/${taskdef_name}/witness.yml - + + + + - + RESULTSDIR/LOGDIR/${rundefinition_name}/${taskdef_name}/witness.yml + - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64-ReachSafety.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp - + + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/ReachSafety-Loops-Simple.set + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp + - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64Large-ReachSafety.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp - + diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint.sh b/sv-comp/my-bench-sv-comp/yaml/goblint.sh index f27daccf69..ffbfa9f1a2 100755 --- a/sv-comp/my-bench-sv-comp/yaml/goblint.sh +++ b/sv-comp/my-bench-sv-comp/yaml/goblint.sh @@ -3,9 +3,9 @@ shopt -s extglob MYBENCHDIR=/mnt/goblint-svcomp/benchexec/my-bench-sv-comp/yaml -RESULTSDIR=/mnt/goblint-svcomp/benchexec/results/yaml-21-software-rebase -GOBLINTPARALLEL=8 -VALIDATEPARALLEL=8 +RESULTSDIR=/mnt/goblint-svcomp/benchexec/results/yaml-xy +GOBLINTPARALLEL=14 +VALIDATEPARALLEL=14 mkdir $RESULTSDIR @@ -30,7 +30,8 @@ benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RES # Merge witness validation results cd $RESULTSDIR # python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint.*.results.*.xml.bz2 goblint-validate-tmp.*.results.*.xml.bz2 -python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint.*.results.sv-comp_prop-reachsafety.xml.bz2 goblint-validate-tmp.*.results.sv-comp_prop-reachsafety.xml.bz2 +python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint.*.results.all.ReachSafety-Loops-Simple.xml.bz2 goblint-validate-tmp.*.results.all.ReachSafety-Loops-Simple.xml.bz2 +python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint.*.results.loop-head.ReachSafety-Loops-Simple.xml.bz2 goblint-validate-tmp.*.results.loop-head.ReachSafety-Loops-Simple.xml.bz2 # Generate table with merged results and witness validation results sed -e "s/LOGDIR/$LOGDIR/" $MYBENCHDIR/table-generator.xml > table-generator.xml diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint.xml b/sv-comp/my-bench-sv-comp/yaml/goblint.xml index 3c8a0ccf30..a4e43e0b2a 100644 --- a/sv-comp/my-bench-sv-comp/yaml/goblint.xml +++ b/sv-comp/my-bench-sv-comp/yaml/goblint.xml @@ -11,23 +11,29 @@ evals = (\d+) - + + + + - + + + + - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64-ReachSafety.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp - + + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/ReachSafety-Loops-Simple.set + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp + - - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/SoftwareSystems-DeviceDriversLinux64Large-ReachSafety.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp - + diff --git a/sv-comp/my-bench-sv-comp/yaml/table-generator.xml b/sv-comp/my-bench-sv-comp/yaml/table-generator.xml index f6d01fc4db..dee86ba723 100644 --- a/sv-comp/my-bench-sv-comp/yaml/table-generator.xml +++ b/sv-comp/my-bench-sv-comp/yaml/table-generator.xml @@ -2,27 +2,48 @@ - - + - witness + witness total: (\d+) - - - solving +([0-9.]+) s + + + solving +([0-9.]+) ?s vars = (\d+) evals = (\d+) - - + - - - solving +([0-9.]+) s + + + solving +([0-9.]+) ?s + vars = (\d+) + evals = (\d+) + + + + + + witness + total: (\d+) + + + + solving +([0-9.]+) ?s + vars = (\d+) + evals = (\d+) + + + + + + + + solving +([0-9.]+) ?s vars = (\d+) evals = (\d+) From 604b05cbb3c893fd7893b1af77be79671c28fe70 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 22 Dec 2022 13:55:31 +0000 Subject: [PATCH 123/132] Limit YAML witness SV-COMP testing to true verdicts --- sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml | 6 +++--- sv-comp/my-bench-sv-comp/yaml/goblint.xml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml b/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml index 9a43316ee2..5a3cac3065 100644 --- a/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml +++ b/sv-comp/my-bench-sv-comp/yaml/goblint-validate.xml @@ -26,17 +26,17 @@ /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/ReachSafety-Loops-Simple.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint.xml b/sv-comp/my-bench-sv-comp/yaml/goblint.xml index a4e43e0b2a..5ca9a58d06 100644 --- a/sv-comp/my-bench-sv-comp/yaml/goblint.xml +++ b/sv-comp/my-bench-sv-comp/yaml/goblint.xml @@ -23,17 +23,17 @@ /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/ReachSafety-Loops-Simple.set - /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp + /mnt/goblint-svcomp/benchexec/sv-benchmarks/c/properties/unreach-call.prp From 8ae3307c53a25dd0c011aff0b138e45ec6eab328 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Dec 2022 12:33:41 +0200 Subject: [PATCH 124/132] Fix YAML witness generation crash on preprocessed file --- 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 bbcdd7357b..5c3aee7098 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -3,7 +3,7 @@ open GoblintCil let uuid_random_state = Random.State.make_self_init () -let sha256_file f = Sha256.(to_hex (file f)) +let sha256_file f = try Sha256.(to_hex (file f)) with Sys_error _ -> "" let sha256_file_cache = BatCache.make_ht ~gen:sha256_file ~init_size:5 let sha256_file = sha256_file_cache.get From baa5b2d56e70092df65c7a53e6fc99c5b18fb2c2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Dec 2022 12:51:12 +0200 Subject: [PATCH 125/132] Fix mutex analysis postsolving crash on smtprc_comb.c --- src/analyses/mutexAnalysis.ml | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 83590ac16a..d54e5ac3bf 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -228,17 +228,19 @@ struct if !GU.postsolving then ( let held_locks = (if write then snd else fst) (G.protecting (ctx.global (V.protecting v))) in - let vs_empty = VarSet.empty () in - Mutexes.iter (fun addr -> - let vs = VarSet.singleton v in - let protected = - if write then - (vs_empty, vs) - else - (vs, vs_empty) - in - ctx.sideg (V.protected addr) (G.create_protected protected) - ) held_locks + if not (Mutexes.is_top held_locks) then ( + let vs_empty = VarSet.empty () in + Mutexes.iter (fun addr -> + let vs = VarSet.singleton v in + let protected = + if write then + (vs_empty, vs) + else + (vs, vs_empty) + in + ctx.sideg (V.protected addr) (G.create_protected protected) + ) held_locks + ) ) | None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound." in From 4b6c4f2202608ead0f6cb6ad8f8794300020d36c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Dec 2022 14:33:21 +0200 Subject: [PATCH 126/132] Fix fixpoint error on thread creation wrapper with local access collecting --- src/analyses/accessAnalysis.ml | 2 +- .../regression/56-witness/06-smtprc-witness-fp.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/regression/56-witness/06-smtprc-witness-fp.c diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index d36726afba..3f7448a83b 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -40,7 +40,7 @@ struct + [deref=true], [reach=true] - Access [exp] by dereferencing transitively (reachable), used for deep special accesses. *) let access_one_top ?(force=false) ?(deref=false) ctx (kind: AccessKind.t) reach exp = if M.tracing then M.traceli "access" "access_one_top %a %b %a:\n" AccessKind.pretty kind reach d_exp exp; - if force || (!collect_local && !Goblintutil.postsolving) || ThreadFlag.is_multi (Analyses.ask_of_ctx ctx) then ( + if force || !collect_local || ThreadFlag.is_multi (Analyses.ask_of_ctx ctx) then ( if deref then do_access ctx kind reach exp; Access.distribute_access_exp (do_access ctx Read false) exp diff --git a/tests/regression/56-witness/06-smtprc-witness-fp.c b/tests/regression/56-witness/06-smtprc-witness-fp.c new file mode 100644 index 0000000000..691decadd3 --- /dev/null +++ b/tests/regression/56-witness/06-smtprc-witness-fp.c @@ -0,0 +1,16 @@ +// PARAM: --enable witness.yaml.enabled --enable witness.invariant.accessed +#include + +int cleaner_start() { + return 0; +} + +void start_scan() { + pthread_t tid; + pthread_create(&tid, NULL, cleaner_start, NULL); +} + +int main() { + start_scan(); // FIXPOINT: mutex:start_scan + return 0; +} From d19117ed73493b4f085d8969d76fbb72c465f21a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Dec 2022 14:33:33 +0200 Subject: [PATCH 127/132] Revert "Fix mutex analysis postsolving crash on smtprc_comb.c" This reverts commit baa5b2d56e70092df65c7a53e6fc99c5b18fb2c2. --- src/analyses/mutexAnalysis.ml | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index d54e5ac3bf..83590ac16a 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -228,19 +228,17 @@ struct if !GU.postsolving then ( let held_locks = (if write then snd else fst) (G.protecting (ctx.global (V.protecting v))) in - if not (Mutexes.is_top held_locks) then ( - let vs_empty = VarSet.empty () in - Mutexes.iter (fun addr -> - let vs = VarSet.singleton v in - let protected = - if write then - (vs_empty, vs) - else - (vs, vs_empty) - in - ctx.sideg (V.protected addr) (G.create_protected protected) - ) held_locks - ) + let vs_empty = VarSet.empty () in + Mutexes.iter (fun addr -> + let vs = VarSet.singleton v in + let protected = + if write then + (vs_empty, vs) + else + (vs, vs_empty) + in + ctx.sideg (V.protected addr) (G.create_protected protected) + ) held_locks ) | None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound." in From be4a51fc9e4b8a6de2572fc1a4dbd055d4b670a2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Dec 2022 12:43:49 +0000 Subject: [PATCH 128/132] Add BenchExec for goblint-bench pthread programs with YAML witnesses --- .../yaml/goblint-bench-validate.xml | 31 +++++++++++ .../my-bench-sv-comp/yaml/goblint-bench.sh | 42 +++++++++++++++ .../my-bench-sv-comp/yaml/goblint-bench.xml | 28 ++++++++++ .../yaml/table-generator-bench.xml | 51 +++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 sv-comp/my-bench-sv-comp/yaml/goblint-bench-validate.xml create mode 100755 sv-comp/my-bench-sv-comp/yaml/goblint-bench.sh create mode 100644 sv-comp/my-bench-sv-comp/yaml/goblint-bench.xml create mode 100644 sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint-bench-validate.xml b/sv-comp/my-bench-sv-comp/yaml/goblint-bench-validate.xml new file mode 100644 index 0000000000..dfb24a2ae3 --- /dev/null +++ b/sv-comp/my-bench-sv-comp/yaml/goblint-bench-validate.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + evals = (\d+) + + + + + + + + + + + + + RESULTSDIR/LOGDIR/${rundefinition_name}/${taskdef_name}/witness.yml + + + + /mnt/goblint-svcomp/goblint-bench/bench/Pthread.set + + + diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint-bench.sh b/sv-comp/my-bench-sv-comp/yaml/goblint-bench.sh new file mode 100755 index 0000000000..ca37845ced --- /dev/null +++ b/sv-comp/my-bench-sv-comp/yaml/goblint-bench.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +shopt -s extglob + +MYBENCHDIR=/mnt/goblint-svcomp/benchexec/my-bench-sv-comp/yaml +RESULTSDIR=/mnt/goblint-svcomp/benchexec/results/yaml-xy-bench +GOBLINTPARALLEL=14 +VALIDATEPARALLEL=14 + +mkdir $RESULTSDIR + +# Run verification +cd /mnt/goblint-svcomp/sv-comp/goblint +# read-only and overlay dirs for Value too large for defined data type workaround +benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $GOBLINTPARALLEL $MYBENCHDIR/goblint-bench.xml + +# Extract witness directory +cd $RESULTSDIR +LOGDIR=`echo goblint-bench.*.files` +echo $LOGDIR + +# Construct validation XMLs +cd $MYBENCHDIR +sed -e "s|RESULTSDIR|$RESULTSDIR|" -e "s/LOGDIR/$LOGDIR/" goblint-bench-validate.xml > goblint-bench-validate-tmp.xml + +# Run validation +cd /mnt/goblint-svcomp/sv-comp/goblint +benchexec --read-only-dir / --overlay-dir . --hidden-dir /home --outputpath $RESULTSDIR --numOfThreads $VALIDATEPARALLEL $MYBENCHDIR/goblint-bench-validate-tmp.xml + +# Merge witness validation results +cd $RESULTSDIR +# python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint.*.results.*.xml.bz2 goblint-validate-tmp.*.results.*.xml.bz2 +python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint-bench.*.results.all.Pthread.xml.bz2 goblint-bench-validate-tmp.*.results.all.Pthread.xml.bz2 +python3 /mnt/goblint-svcomp/benchexec/benchexec/contrib/mergeBenchmarkSets.py -o . goblint-bench.*.results.loop-head.Pthread.xml.bz2 goblint-bench-validate-tmp.*.results.loop-head.Pthread.xml.bz2 + +# Generate table with merged results and witness validation results +sed -e "s/LOGDIR/$LOGDIR/" $MYBENCHDIR/table-generator-bench.xml > table-generator.xml +table-generator -x table-generator.xml + +# Decompress all tool outputs for table HTML links +unzip -o goblint-bench.*.logfiles.zip +unzip -o goblint-bench-validate-tmp.*.logfiles.zip diff --git a/sv-comp/my-bench-sv-comp/yaml/goblint-bench.xml b/sv-comp/my-bench-sv-comp/yaml/goblint-bench.xml new file mode 100644 index 0000000000..547d67b8cd --- /dev/null +++ b/sv-comp/my-bench-sv-comp/yaml/goblint-bench.xml @@ -0,0 +1,28 @@ + + + + + **.yml + + + + + + evals = (\d+) + + + + + + + + + + + + + + /mnt/goblint-svcomp/goblint-bench/bench/Pthread.set + + + diff --git a/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml b/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml new file mode 100644 index 0000000000..1e3dd204b1 --- /dev/null +++ b/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml @@ -0,0 +1,51 @@ + + +
+ + + + + witness + total: (\d+) + + + + solving +([0-9.]+) ?s + vars = (\d+) + evals = (\d+) + + + + + + + + solving +([0-9.]+) ?s + vars = (\d+) + evals = (\d+) + + + + + + witness + total: (\d+) + + + + solving +([0-9.]+) ?s + vars = (\d+) + evals = (\d+) + + + + + + + + solving +([0-9.]+) ?s + vars = (\d+) + evals = (\d+) + + +
From 926bd9f34eeb18f73b00e0ac3011f101751a3e71 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Dec 2022 17:08:30 +0200 Subject: [PATCH 129/132] Make summary message totals distinct for grepping --- src/analyses/raceAnalysis.ml | 2 +- src/framework/control.ml | 2 +- src/witness/yamlWitness.ml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index f65ee3e6d9..bfff9ce4e3 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -160,7 +160,7 @@ struct (Pretty.dprintf "safe: %d" !safe, None); (Pretty.dprintf "vulnerable: %d" !vulnerable, None); (Pretty.dprintf "unsafe: %d" !unsafe, None); - (Pretty.dprintf "total: %d" total, None); + (Pretty.dprintf "total memory locations: %d" total, None); ]; ) end diff --git a/src/framework/control.ml b/src/framework/control.ml index d10a5d9af2..abccb871f2 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -165,7 +165,7 @@ struct M.msg_group severity ~category:Deadcode "Logical lines of code (LLoC) summary" [ (Pretty.dprintf "live: %d" live_count, None); (Pretty.dprintf "dead: %d%s" dead_total (if uncalled_fn_loc > 0 then Printf.sprintf " (%d in uncalled functions)" uncalled_fn_loc else ""), None); - (Pretty.dprintf "total: %d" total, None); + (Pretty.dprintf "total lines: %d" total, None); ] ); NH.mem live_nodes diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 5c3aee7098..c7116bf03c 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -383,7 +383,7 @@ struct let yaml_entries = List.rev_map YamlWitnessType.Entry.to_yaml entries in (* reverse to make entries in file in the same order as generation messages *) M.msg_group Info ~category:Witness "witness generation summary" [ - (Pretty.dprintf "total: %d" (List.length yaml_entries), None); + (Pretty.dprintf "total generation entries: %d" (List.length yaml_entries), None); ]; yaml_entries_to_file yaml_entries (Fpath.v (GobConfig.get_string "witness.yaml.path")) @@ -652,7 +652,7 @@ struct (Pretty.dprintf "unchecked: %d" !cnt_unchecked, None); (Pretty.dprintf "unsupported: %d" !cnt_unsupported, None); (Pretty.dprintf "disabled: %d" !cnt_disabled, None); - (Pretty.dprintf "total: %d" (!cnt_confirmed + !cnt_unconfirmed + !cnt_refuted + !cnt_unchecked + !cnt_unsupported + !cnt_error + !cnt_disabled), None); + (Pretty.dprintf "total validation entries: %d" (!cnt_confirmed + !cnt_unconfirmed + !cnt_refuted + !cnt_unchecked + !cnt_unsupported + !cnt_error + !cnt_disabled), None); ]; yaml_entries_to_file (List.rev yaml_entries') (Fpath.v (GobConfig.get_string "witness.yaml.certificate")) From da509ad721c22ab96e2c0a6c6235006b74f7d680 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Dec 2022 17:12:07 +0200 Subject: [PATCH 130/132] Update BenchExec YAML witness entries column regexes --- sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml | 4 ++-- sv-comp/my-bench-sv-comp/yaml/table-generator.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml b/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml index 1e3dd204b1..f0ae18aeef 100644 --- a/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml +++ b/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml @@ -6,7 +6,7 @@ witness - total: (\d+) + total generation entries: (\d+) @@ -29,7 +29,7 @@ witness - total: (\d+) + total generation entries: (\d+) diff --git a/sv-comp/my-bench-sv-comp/yaml/table-generator.xml b/sv-comp/my-bench-sv-comp/yaml/table-generator.xml index dee86ba723..72b69df94a 100644 --- a/sv-comp/my-bench-sv-comp/yaml/table-generator.xml +++ b/sv-comp/my-bench-sv-comp/yaml/table-generator.xml @@ -6,7 +6,7 @@ witness - total: (\d+) + total generation entries: (\d+) @@ -29,7 +29,7 @@ witness - total: (\d+) + total generation entries: (\d+) From b6f82c9457f8b7a7197c334b0f3f6f7011da9a28 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Dec 2022 15:23:19 +0000 Subject: [PATCH 131/132] Add live lines columns to YAML witness BenchExec --- sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml | 4 ++++ sv-comp/my-bench-sv-comp/yaml/table-generator.xml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml b/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml index f0ae18aeef..4bf2676eef 100644 --- a/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml +++ b/sv-comp/my-bench-sv-comp/yaml/table-generator-bench.xml @@ -13,6 +13,7 @@ solving +([0-9.]+) ?s vars = (\d+) evals = (\d+) + live: (\d+) @@ -23,6 +24,7 @@ solving +([0-9.]+) ?s vars = (\d+) evals = (\d+) + live: (\d+) @@ -36,6 +38,7 @@ solving +([0-9.]+) ?s vars = (\d+) evals = (\d+) + live: (\d+) @@ -46,6 +49,7 @@ solving +([0-9.]+) ?s vars = (\d+) evals = (\d+) + live: (\d+) diff --git a/sv-comp/my-bench-sv-comp/yaml/table-generator.xml b/sv-comp/my-bench-sv-comp/yaml/table-generator.xml index 72b69df94a..790fac39b0 100644 --- a/sv-comp/my-bench-sv-comp/yaml/table-generator.xml +++ b/sv-comp/my-bench-sv-comp/yaml/table-generator.xml @@ -13,6 +13,7 @@ solving +([0-9.]+) ?s vars = (\d+) evals = (\d+) + live: (\d+) @@ -23,6 +24,7 @@ solving +([0-9.]+) ?s vars = (\d+) evals = (\d+) + live: (\d+) @@ -36,6 +38,7 @@ solving +([0-9.]+) ?s vars = (\d+) evals = (\d+) + live: (\d+) @@ -46,6 +49,7 @@ solving +([0-9.]+) ?s vars = (\d+) evals = (\d+) + live: (\d+) From 6e76ea6a2b7af71b9a15fc6e972e61e1f0578b0c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 27 Dec 2022 18:19:20 +0200 Subject: [PATCH 132/132] Promote total changes to cram tests --- tests/regression/00-sanity/01-assert.t | 2 +- tests/regression/04-mutex/01-simple_rc.t | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/00-sanity/01-assert.t b/tests/regression/00-sanity/01-assert.t index 820c890419..a0a26e4bed 100644 --- a/tests/regression/00-sanity/01-assert.t +++ b/tests/regression/00-sanity/01-assert.t @@ -7,4 +7,4 @@ [Warning][Deadcode] Logical lines of code (LLoC) summary: live: 7 dead: 2 - total: 9 + total lines: 9 diff --git a/tests/regression/04-mutex/01-simple_rc.t b/tests/regression/04-mutex/01-simple_rc.t index 042ce27641..c77cf1074c 100644 --- a/tests/regression/04-mutex/01-simple_rc.t +++ b/tests/regression/04-mutex/01-simple_rc.t @@ -2,7 +2,7 @@ [Info][Deadcode] Logical lines of code (LLoC) summary: live: 12 dead: 0 - total: 12 + total lines: 12 [Warning][Race] Memory location myglobal@01-simple_rc.c:4:5-4:13 (race with conf. 110): write with [mhp:{tid=[main, t_fun@01-simple_rc.c:17:3-17:40]}, lock:{mutex1}, thread:[main, t_fun@01-simple_rc.c:17:3-17:40]] (conf. 110) (01-simple_rc.c:10:3-10:22) @@ -13,4 +13,4 @@ safe: 0 vulnerable: 0 unsafe: 1 - total: 1 + total memory locations: 1