From 2bc950b41a13fad70b31c1b12bd112a76699ac36 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 14:50:46 +0300 Subject: [PATCH 001/566] Add yamlWitnessStrip testing utility --- src/witness/yamlWitnessType.ml | 9 +++++++ tests/util/dune | 4 +++ tests/util/yamlWitnessStrip.ml | 46 ++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 tests/util/dune create mode 100644 tests/util/yamlWitnessStrip.ml diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index a4f80bab02..920cd89eaf 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -105,6 +105,7 @@ struct column: int; function_: string; } + [@@deriving ord] let to_yaml {file_name; file_hash; line; column; function_} = `O [ @@ -132,6 +133,7 @@ struct type_: string; format: string; } + [@@deriving ord] let to_yaml {string; type_; format} = `O [ @@ -154,6 +156,7 @@ struct location: Location.t; loop_invariant: Invariant.t; } + [@@deriving ord] let entry_type = "loop_invariant" @@ -175,6 +178,7 @@ struct type t = { flow_insensitive_invariant: Invariant.t; } + [@@deriving ord] let entry_type = "flow_insensitive_invariant" @@ -196,6 +200,7 @@ struct loop_invariant: Invariant.t; precondition: Invariant.t; } + [@@deriving ord] let entry_type = "precondition_loop_invariant" @@ -221,6 +226,7 @@ struct type_: string; file_hash: string; } + [@@deriving ord] let to_yaml {uuid; type_; file_hash} = `O [ @@ -244,6 +250,7 @@ struct type_: string; format: string; } + [@@deriving ord] let to_yaml {string; type_; format} = `O [ @@ -266,6 +273,7 @@ struct target: Target.t; certification: Certification.t; } + [@@deriving ord] let entry_type = "loop_invariant_certificate" @@ -297,6 +305,7 @@ struct | PreconditionLoopInvariant of PreconditionLoopInvariant.t | LoopInvariantCertificate of LoopInvariantCertificate.t | PreconditionLoopInvariantCertificate of PreconditionLoopInvariantCertificate.t + [@@deriving ord] let entry_type = function | LoopInvariant _ -> LoopInvariant.entry_type diff --git a/tests/util/dune b/tests/util/dune new file mode 100644 index 0000000000..0f5f54d2b5 --- /dev/null +++ b/tests/util/dune @@ -0,0 +1,4 @@ +(executable + (name yamlWitnessStrip) + (libraries goblint_lib goblint.sites.dune batteries.unthreaded) + (preprocess (pps ppx_deriving.std))) diff --git a/tests/util/yamlWitnessStrip.ml b/tests/util/yamlWitnessStrip.ml new file mode 100644 index 0000000000..0945fb3bc6 --- /dev/null +++ b/tests/util/yamlWitnessStrip.ml @@ -0,0 +1,46 @@ +open Goblint_lib +open YamlWitnessType + +module StrippedEntry = +struct + type t = { + entry_type: EntryType.t; + } + [@@deriving ord] + + let to_yaml {entry_type} = + `O ([ + ("entry_type", `String (EntryType.entry_type entry_type)); + ] @ EntryType.to_yaml' entry_type) +end + +(* Use set for output, so order is deterministic regardless of Goblint. *) +module StrippedEntrySet = Set.Make (StrippedEntry) + +let main () = + let yaml_str = Batteries.input_all stdin in + let yaml = Yaml.of_string_exn yaml_str in + let yaml_entries = yaml |> GobYaml.list |> BatResult.get_ok in + + let entries' = List.fold_left (fun entries' yaml_entry -> + match YamlWitnessType.Entry.of_yaml yaml_entry with + | Ok {entry_type; _} -> + let stripped_entry: StrippedEntry.t = {entry_type} in + StrippedEntrySet.add stripped_entry entries' + | Error (`Msg e) -> + Format.eprintf "couldn't parse entry: %s" e; + entries' + ) StrippedEntrySet.empty yaml_entries + in + let yaml_entries' = + StrippedEntrySet.elements entries' + |> List.map StrippedEntry.to_yaml + in + + let yaml' = `A (List.rev yaml_entries') in + (* 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 + Batteries.output_string Batteries.stdout text + +let () = main () From 74477a4cea36845a37f98a47708f67b267652a55 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 14:51:14 +0300 Subject: [PATCH 002/566] Add cram witness test to 56-witness/05-prec-problem --- tests/regression/56-witness/05-prec-problem.c | 2 +- tests/regression/56-witness/05-prec-problem.t | 157 ++++++++++++++++++ tests/regression/56-witness/dune | 6 + 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 tests/regression/56-witness/05-prec-problem.t create mode 100644 tests/regression/56-witness/dune diff --git a/tests/regression/56-witness/05-prec-problem.c b/tests/regression/56-witness/05-prec-problem.c index 6bbc8b09d3..120c211780 100644 --- a/tests/regression/56-witness/05-prec-problem.c +++ b/tests/regression/56-witness/05-prec-problem.c @@ -1,4 +1,4 @@ -//PARAM: --enable witness.yaml.enabled --enable ana.int.interval +//PARAM: --enable ana.int.interval #include int foo(int* ptr1, int* ptr2){ diff --git a/tests/regression/56-witness/05-prec-problem.t b/tests/regression/56-witness/05-prec-problem.t new file mode 100644 index 0000000000..2387cf00e3 --- /dev/null +++ b/tests/regression/56-witness/05-prec-problem.t @@ -0,0 +1,157 @@ + $ goblint --enable witness.yaml.enabled --enable ana.int.interval 05-prec-problem.c + [Success][Assert] Assertion "y != z" will succeed (05-prec-problem.c:20:5-20:28) + Live lines: 12 + No lines with dead code found by solver. + Total lines (logical LoC): 12 + [Info][Witness] witness generation summary: + total: 15 + +Witness shouldn't contain two unsound precondition_loop_invariant-s with precondition `*ptr1 == 5 && *ptr2 == 5`, +and separately invariants `result == 0` and `result == 1`. +The sound invariant is `result == 1 || result == 0`. + + $ yamlWitnessStrip < witness.yml + - entry_type: precondition_loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 12 + column: 4 + function: foo + loop_invariant: + string: result == 1 || result == 0 + type: assertion + format: C + precondition: + string: '*ptr1 == 5 && *ptr2 == 5' + type: assertion + format: C + - entry_type: precondition_loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 6 + column: 7 + function: foo + loop_invariant: + string: '*ptr2 == 5' + type: assertion + format: C + precondition: + string: '*ptr1 == 5 && *ptr2 == 5' + type: assertion + format: C + - entry_type: precondition_loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 6 + column: 7 + function: foo + loop_invariant: + string: '*ptr1 == 5' + type: assertion + format: C + precondition: + string: '*ptr1 == 5 && *ptr2 == 5' + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 20 + column: 4 + function: main + loop_invariant: + string: z == 1 + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 20 + column: 4 + function: main + loop_invariant: + string: y == 0 + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 19 + column: 8 + function: main + loop_invariant: + string: y == 0 + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 18 + column: 8 + function: main + loop_invariant: + string: five2 == 5 + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 17 + column: 8 + function: main + loop_invariant: + string: five == 5 + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 12 + column: 4 + function: foo + loop_invariant: + string: result <= 1 + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 12 + column: 4 + function: foo + loop_invariant: + string: 0 <= result + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 6 + column: 7 + function: foo + loop_invariant: + string: '*ptr2 == 5 || *ptr2 == 5' + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 05-prec-problem.c + file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + line: 6 + column: 7 + function: foo + loop_invariant: + string: '*ptr1 == 5' + type: assertion + format: C diff --git a/tests/regression/56-witness/dune b/tests/regression/56-witness/dune new file mode 100644 index 0000000000..05fce8ddec --- /dev/null +++ b/tests/regression/56-witness/dune @@ -0,0 +1,6 @@ +(env + (_ + (binaries ../../util/yamlWitnessStrip.exe))) + +(cram + (deps (glob_files *.c) %{bin:yamlWitnessStrip})) From d8b0c1efee3246376cd1355f20dc5a8c1d29e46c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 15:10:49 +0300 Subject: [PATCH 003/566] Strip file hashes from YAML witness cram tests --- tests/regression/56-witness/05-prec-problem.c | 2 +- tests/regression/56-witness/05-prec-problem.t | 24 +++++++++---------- tests/util/yamlWitnessStrip.ml | 24 +++++++++++++++++++ 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/tests/regression/56-witness/05-prec-problem.c b/tests/regression/56-witness/05-prec-problem.c index 120c211780..ec1a8fc3d4 100644 --- a/tests/regression/56-witness/05-prec-problem.c +++ b/tests/regression/56-witness/05-prec-problem.c @@ -8,7 +8,7 @@ int foo(int* ptr1, int* ptr2){ } else { result = 1; } - // Look at the generated witness.yml to check whether there are contradictory precondition_loop_invariant[s] + // cram test checks for precondition invariant soundness return result; } diff --git a/tests/regression/56-witness/05-prec-problem.t b/tests/regression/56-witness/05-prec-problem.t index 2387cf00e3..33a625696f 100644 --- a/tests/regression/56-witness/05-prec-problem.t +++ b/tests/regression/56-witness/05-prec-problem.t @@ -14,7 +14,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: precondition_loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 12 column: 4 function: foo @@ -29,7 +29,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: precondition_loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 6 column: 7 function: foo @@ -44,7 +44,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: precondition_loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 6 column: 7 function: foo @@ -59,7 +59,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 20 column: 4 function: main @@ -70,7 +70,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 20 column: 4 function: main @@ -81,7 +81,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 19 column: 8 function: main @@ -92,7 +92,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 18 column: 8 function: main @@ -103,7 +103,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 17 column: 8 function: main @@ -114,7 +114,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 12 column: 4 function: foo @@ -125,7 +125,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 12 column: 4 function: foo @@ -136,7 +136,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 6 column: 7 function: foo @@ -147,7 +147,7 @@ The sound invariant is `result == 1 || result == 0`. - entry_type: loop_invariant location: file_name: 05-prec-problem.c - file_hash: cf828ba93b8ed3289734bd38dd37f9e9fd808af7d303b61191df6737aa478928 + file_hash: $STRIPPED_FILE_HASH line: 6 column: 7 function: foo diff --git a/tests/util/yamlWitnessStrip.ml b/tests/util/yamlWitnessStrip.ml index 0945fb3bc6..5beb0f6ebe 100644 --- a/tests/util/yamlWitnessStrip.ml +++ b/tests/util/yamlWitnessStrip.ml @@ -8,6 +8,29 @@ struct } [@@deriving ord] + let strip_file_hashes {entry_type} = + let stripped_file_hash = "$STRIPPED_FILE_HASH" in + let location_strip_file_hash location: Location.t = + {location with file_hash = stripped_file_hash} + in + let target_strip_file_hash target: Target.t = + {target with file_hash = stripped_file_hash} + in + let entry_type: EntryType.t = + match entry_type with + | LoopInvariant x -> + LoopInvariant {x with location = location_strip_file_hash x.location} + | PreconditionLoopInvariant x -> + PreconditionLoopInvariant {x with location = location_strip_file_hash x.location} + | LoopInvariantCertificate x -> + LoopInvariantCertificate {x with target = target_strip_file_hash x.target} + | PreconditionLoopInvariantCertificate x -> + PreconditionLoopInvariantCertificate {x with target = target_strip_file_hash x.target} + | _ -> + entry_type + in + {entry_type} + let to_yaml {entry_type} = `O ([ ("entry_type", `String (EntryType.entry_type entry_type)); @@ -34,6 +57,7 @@ let main () = in let yaml_entries' = StrippedEntrySet.elements entries' + |> List.map StrippedEntry.strip_file_hashes |> List.map StrippedEntry.to_yaml in From 9086c995d95d48be4b3c299632e374b7fcf821a6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 17:16:49 +0300 Subject: [PATCH 004/566] Add YAML witness privatized invariant tests --- tests/regression/13-privatized/01-priv_nr.t | 218 ++++++++++++++++++ tests/regression/13-privatized/dune | 2 + .../regression/36-apron/12-traces-min-rpb1.t | 168 ++++++++++++++ tests/regression/36-apron/dune | 2 + tests/regression/56-witness/dune | 6 +- tests/regression/dune | 9 +- 6 files changed, 399 insertions(+), 6 deletions(-) create mode 100644 tests/regression/13-privatized/01-priv_nr.t create mode 100644 tests/regression/13-privatized/dune create mode 100644 tests/regression/36-apron/12-traces-min-rpb1.t create mode 100644 tests/regression/36-apron/dune diff --git a/tests/regression/13-privatized/01-priv_nr.t b/tests/regression/13-privatized/01-priv_nr.t new file mode 100644 index 0000000000..102c692366 --- /dev/null +++ b/tests/regression/13-privatized/01-priv_nr.t @@ -0,0 +1,218 @@ +`protection` privatization: + + $ goblint --enable witness.yaml.enabled --disable witness.invariant.other --set ana.base.privatization protection 01-priv_nr.c + [Success][Assert] Assertion "glob1 == 5" will succeed (01-priv_nr.c:22:3-22:30) + [Success][Assert] Assertion "t == 5" will succeed (01-priv_nr.c:12:3-12:26) + [Success][Assert] Assertion "glob1 == -10" will succeed (01-priv_nr.c:14:3-14:32) + [Success][Assert] Assertion "glob1 == 6" will succeed (01-priv_nr.c:26:3-26:30) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total: 19 + [Info][Witness] witness generation summary: + total: 4 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: precondition_loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 25 + column: 2 + function: main + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + precondition: + string: glob1 == 5 + type: assertion + format: C + - entry_type: precondition_loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 11 + column: 2 + function: t_fun + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + precondition: + string: (unsigned long )arg == 0UL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 25 + column: 2 + function: main + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 11 + column: 2 + function: t_fun + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + +`mutex-meet` privatization: + + $ goblint --enable witness.yaml.enabled --disable witness.invariant.other --set ana.base.privatization mutex-meet 01-priv_nr.c + [Success][Assert] Assertion "glob1 == 5" will succeed (01-priv_nr.c:22:3-22:30) + [Success][Assert] Assertion "t == 5" will succeed (01-priv_nr.c:12:3-12:26) + [Success][Assert] Assertion "glob1 == -10" will succeed (01-priv_nr.c:14:3-14:32) + [Success][Assert] Assertion "glob1 == 6" will succeed (01-priv_nr.c:26:3-26:30) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total: 19 + [Info][Witness] witness generation summary: + total: 4 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: precondition_loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 25 + column: 2 + function: main + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + precondition: + string: glob1 == 5 + type: assertion + format: C + - entry_type: precondition_loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 11 + column: 2 + function: t_fun + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + precondition: + string: (unsigned long )arg == 0UL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 25 + column: 2 + function: main + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 11 + column: 2 + function: t_fun + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + +`write+lock` privatization: + + $ goblint --enable witness.yaml.enabled --disable witness.invariant.other --set ana.base.privatization write+lock 01-priv_nr.c + [Success][Assert] Assertion "glob1 == 5" will succeed (01-priv_nr.c:22:3-22:30) + [Success][Assert] Assertion "t == 5" will succeed (01-priv_nr.c:12:3-12:26) + [Success][Assert] Assertion "glob1 == -10" will succeed (01-priv_nr.c:14:3-14:32) + [Success][Assert] Assertion "glob1 == 6" will succeed (01-priv_nr.c:26:3-26:30) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total: 19 + [Info][Witness] witness generation summary: + total: 4 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: precondition_loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 25 + column: 2 + function: main + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + precondition: + string: glob1 == 5 + type: assertion + format: C + - entry_type: precondition_loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 11 + column: 2 + function: t_fun + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + precondition: + string: (unsigned long )arg == 0UL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 25 + column: 2 + function: main + loop_invariant: + string: glob1 == 5 + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 01-priv_nr.c + file_hash: $STRIPPED_FILE_HASH + line: 11 + column: 2 + function: t_fun + loop_invariant: + string: glob1 == 5 + type: assertion + format: C diff --git a/tests/regression/13-privatized/dune b/tests/regression/13-privatized/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/13-privatized/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) diff --git a/tests/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t new file mode 100644 index 0000000000..7292c90f48 --- /dev/null +++ b/tests/regression/36-apron/12-traces-min-rpb1.t @@ -0,0 +1,168 @@ + $ goblint --enable witness.yaml.enabled --disable witness.invariant.other --disable ana.base.invariant.enabled --set ana.apron.privatization mutex-meet --set ana.activated[+] apron --enable ana.sv-comp.functions --set ana.apron.domain polyhedra 12-traces-min-rpb1.c + [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:16:3-16:26) + [Warning][Assert] Assertion "g == h" is unknown. (12-traces-min-rpb1.c:27:3-27:26) + [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:29:3-29:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 18 + dead: 0 + total: 18 + [Warning][Race] Memory location g@12-traces-min-rpb1.c:7:5-7:10 (race with conf. 110): + write with [mhp:{tid=[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}, + lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (12-traces-min-rpb1.c:14:3-14:8) + read with [mhp:{tid=[main]; created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (12-traces-min-rpb1.c:27:3-27:26) + [Warning][Race] Memory location h@12-traces-min-rpb1.c:8:5-8:10 (race with conf. 110): + write with [mhp:{tid=[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}, lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (12-traces-min-rpb1.c:15:3-15:8) + read with [mhp:{tid=[main]; created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (12-traces-min-rpb1.c:27:3-27:26) + [Info][Witness] witness generation summary: + total: 12 + [Info][Race] Memory locations race summary: + safe: 0 + vulnerable: 0 + unsafe: 2 + total: 2 + + $ yamlWitnessStrip < witness.yml + - entry_type: precondition_loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 29 + column: 2 + function: main + loop_invariant: + string: 2147483648LL + (long long )g >= 0LL + type: assertion + format: C + precondition: + string: -1LL + (long long )h == 0LL && -1LL + (long long )g == 0LL + type: assertion + format: C + - entry_type: precondition_loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 29 + column: 2 + function: main + loop_invariant: + string: 2147483647LL - (long long )g >= 0LL + type: assertion + format: C + precondition: + string: -1LL + (long long )h == 0LL && -1LL + (long long )g == 0LL + type: assertion + format: C + - entry_type: precondition_loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 29 + column: 2 + function: main + loop_invariant: + string: (0LL - (long long )g) + (long long )h == 0LL + type: assertion + format: C + precondition: + string: -1LL + (long long )h == 0LL && -1LL + (long long )g == 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 29 + column: 2 + function: main + loop_invariant: + string: 2147483648LL + (long long )g >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 29 + column: 2 + function: main + loop_invariant: + string: 2147483647LL - (long long )g >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 29 + column: 2 + function: main + loop_invariant: + string: (0LL - (long long )g) + (long long )h == 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 19 + column: 2 + function: t_fun + loop_invariant: + string: 2147483648LL + (long long )g >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 19 + column: 2 + function: t_fun + loop_invariant: + string: 2147483647LL - (long long )g >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 19 + column: 2 + function: t_fun + loop_invariant: + string: (0LL - (long long )g) + (long long )h == 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 14 + column: 2 + function: t_fun + loop_invariant: + string: 2147483648LL + (long long )g >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 14 + column: 2 + function: t_fun + loop_invariant: + string: 2147483647LL - (long long )g >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $STRIPPED_FILE_HASH + line: 14 + column: 2 + function: t_fun + loop_invariant: + string: (0LL - (long long )g) + (long long )h == 0LL + type: assertion + format: C diff --git a/tests/regression/36-apron/dune b/tests/regression/36-apron/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/36-apron/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) diff --git a/tests/regression/56-witness/dune b/tests/regression/56-witness/dune index 05fce8ddec..23c0dd3290 100644 --- a/tests/regression/56-witness/dune +++ b/tests/regression/56-witness/dune @@ -1,6 +1,2 @@ -(env - (_ - (binaries ../../util/yamlWitnessStrip.exe))) - (cram - (deps (glob_files *.c) %{bin:yamlWitnessStrip})) + (deps (glob_files *.c))) diff --git a/tests/regression/dune b/tests/regression/dune index fdb1d941c2..ff8d68f8c4 100644 --- a/tests/regression/dune +++ b/tests/regression/dune @@ -1,3 +1,10 @@ +(env + (_ + (binaries ../util/yamlWitnessStrip.exe))) + (cram (applies_to :whole_subtree) - (deps %{bin:goblint} (package goblint))) ; need entire package for includes/ + (deps + %{bin:goblint} + (package goblint) ; need entire package for includes/ + %{bin:yamlWitnessStrip})) From 950cf9b1e3679ad2bb53baa4b270038445c20500 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 17:21:26 +0300 Subject: [PATCH 005/566] Fix variable counting with ana.apron.invariant.one-var --- src/analyses/apron/apronAnalysis.apron.ml | 2 +- src/cdomains/apron/apronDomain.apron.ml | 9 ++ .../regression/36-apron/12-traces-min-rpb1.t | 113 +----------------- 3 files changed, 11 insertions(+), 113 deletions(-) diff --git a/src/analyses/apron/apronAnalysis.apron.ml b/src/analyses/apron/apronAnalysis.apron.ml index eab32b3b5b..4de1ced337 100644 --- a/src/analyses/apron/apronAnalysis.apron.ml +++ b/src/analyses/apron/apronAnalysis.apron.ml @@ -542,7 +542,7 @@ struct |> List.enum |> Enum.filter_map (fun (lincons1: Lincons1.t) -> (* filter one-vars *) - if one_var || Apron.Linexpr0.get_size lincons1.lincons0.linexpr0 >= 2 then + if one_var || Lincons1.num_vars lincons1 >= 2 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) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 5f01aef297..37d369c970 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -158,6 +158,15 @@ struct let show = Format.asprintf "%a" print let compare x y = String.compare (show x) (show y) (* HACK *) + + let num_vars x = + (* Apron.Linexpr0.get_size returns some internal nonsense, so we count ourselves. *) + let size = ref 0 in + Lincons1.iter (fun coeff var -> + if not (Apron.Coeff.is_zero coeff) then + incr size + ) x; + !size end module Lincons1Set = diff --git a/tests/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t index 7292c90f48..f20caf1f5c 100644 --- a/tests/regression/36-apron/12-traces-min-rpb1.t +++ b/tests/regression/36-apron/12-traces-min-rpb1.t @@ -14,7 +14,7 @@ write with [mhp:{tid=[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}, lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (12-traces-min-rpb1.c:15:3-15:8) read with [mhp:{tid=[main]; created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (12-traces-min-rpb1.c:27:3-27:26) [Info][Witness] witness generation summary: - total: 12 + total: 3 [Info][Race] Memory locations race summary: safe: 0 vulnerable: 0 @@ -22,73 +22,6 @@ total: 2 $ yamlWitnessStrip < witness.yml - - entry_type: precondition_loop_invariant - location: - file_name: 12-traces-min-rpb1.c - file_hash: $STRIPPED_FILE_HASH - line: 29 - column: 2 - function: main - loop_invariant: - string: 2147483648LL + (long long )g >= 0LL - type: assertion - format: C - precondition: - string: -1LL + (long long )h == 0LL && -1LL + (long long )g == 0LL - type: assertion - format: C - - entry_type: precondition_loop_invariant - location: - file_name: 12-traces-min-rpb1.c - file_hash: $STRIPPED_FILE_HASH - line: 29 - column: 2 - function: main - loop_invariant: - string: 2147483647LL - (long long )g >= 0LL - type: assertion - format: C - precondition: - string: -1LL + (long long )h == 0LL && -1LL + (long long )g == 0LL - type: assertion - format: C - - entry_type: precondition_loop_invariant - location: - file_name: 12-traces-min-rpb1.c - file_hash: $STRIPPED_FILE_HASH - line: 29 - column: 2 - function: main - loop_invariant: - string: (0LL - (long long )g) + (long long )h == 0LL - type: assertion - format: C - precondition: - string: -1LL + (long long )h == 0LL && -1LL + (long long )g == 0LL - type: assertion - format: C - - entry_type: loop_invariant - location: - file_name: 12-traces-min-rpb1.c - file_hash: $STRIPPED_FILE_HASH - line: 29 - column: 2 - function: main - loop_invariant: - string: 2147483648LL + (long long )g >= 0LL - type: assertion - format: C - - entry_type: loop_invariant - location: - file_name: 12-traces-min-rpb1.c - file_hash: $STRIPPED_FILE_HASH - line: 29 - column: 2 - function: main - loop_invariant: - string: 2147483647LL - (long long )g >= 0LL - type: assertion - format: C - entry_type: loop_invariant location: file_name: 12-traces-min-rpb1.c @@ -100,28 +33,6 @@ string: (0LL - (long long )g) + (long long )h == 0LL type: assertion format: C - - entry_type: loop_invariant - location: - file_name: 12-traces-min-rpb1.c - file_hash: $STRIPPED_FILE_HASH - line: 19 - column: 2 - function: t_fun - loop_invariant: - string: 2147483648LL + (long long )g >= 0LL - type: assertion - format: C - - entry_type: loop_invariant - location: - file_name: 12-traces-min-rpb1.c - file_hash: $STRIPPED_FILE_HASH - line: 19 - column: 2 - function: t_fun - loop_invariant: - string: 2147483647LL - (long long )g >= 0LL - type: assertion - format: C - entry_type: loop_invariant location: file_name: 12-traces-min-rpb1.c @@ -133,28 +44,6 @@ string: (0LL - (long long )g) + (long long )h == 0LL type: assertion format: C - - entry_type: loop_invariant - location: - file_name: 12-traces-min-rpb1.c - file_hash: $STRIPPED_FILE_HASH - line: 14 - column: 2 - function: t_fun - loop_invariant: - string: 2147483648LL + (long long )g >= 0LL - type: assertion - format: C - - entry_type: loop_invariant - location: - file_name: 12-traces-min-rpb1.c - file_hash: $STRIPPED_FILE_HASH - line: 14 - column: 2 - function: t_fun - loop_invariant: - string: 2147483647LL - (long long )g >= 0LL - type: assertion - format: C - entry_type: loop_invariant location: file_name: 12-traces-min-rpb1.c From 6023cbd5fa20199e224299b9eb676b2ef0117285 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 17:32:00 +0300 Subject: [PATCH 006/566] Add ana.apron.invariant.diff-box test --- tests/regression/36-apron/52-queuesize.t | 280 +++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 tests/regression/36-apron/52-queuesize.t diff --git a/tests/regression/36-apron/52-queuesize.t b/tests/regression/36-apron/52-queuesize.t new file mode 100644 index 0000000000..d984535e64 --- /dev/null +++ b/tests/regression/36-apron/52-queuesize.t @@ -0,0 +1,280 @@ +`ana.apron.invariant.diff-box` test case from https://github.com/goblint/analyzer/pull/762. + +Without diff-box: + + $ goblint --enable witness.yaml.enabled --disable witness.invariant.other --disable ana.base.invariant.enabled --set ana.apron.privatization mutex-meet --set ana.activated[+] apron --enable ana.sv-comp.functions --set ana.apron.domain polyhedra --enable ana.apron.invariant.one-var --disable ana.apron.invariant.diff-box 52-queuesize.c + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:67:5-67:31) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:68:5-68:38) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:69:5-69:31) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:70:5-70:38) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:71:5-71:45) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:15:3-15:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:16:3-16:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:17:3-17:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:18:3-18:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:19:3-19:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:26:3-26:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:27:3-27:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:28:3-28:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:29:3-29:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:30:3-30:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:36:3-36:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:37:3-37:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:38:3-38:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:39:3-39:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:40:3-40:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:47:3-47:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:48:3-48:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:49:3-49:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:50:3-50:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:51:3-51:43) + [Warning][Deadcode] Function 'worker' has dead code: + on line 58 (52-queuesize.c:58-58) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 53 + dead: 1 + total: 54 + [Warning][Deadcode][CWE-571] condition '1' is always true (52-queuesize.c:56:10-56:11) + [Warning][Deadcode][CWE-571] condition '1' is always true (52-queuesize.c:78:12-78:13) + [Info][Witness] witness generation summary: + total: 8 + [Info][Race] Memory locations race summary: + safe: 3 + vulnerable: 0 + unsafe: 0 + total: 3 + + $ yamlWitnessStrip < witness.yml | tee witness-disable-diff-box.yml + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 36 + column: 2 + function: push + loop_invariant: + string: 2147483647LL - (long long )capacity >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 36 + column: 2 + function: push + loop_invariant: + string: (long long )free >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 36 + column: 2 + function: push + loop_invariant: + string: (long long )capacity - (long long )free >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 36 + column: 2 + function: push + loop_invariant: + string: ((0LL - (long long )capacity) + (long long )free) + (long long )used == + 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 15 + column: 2 + function: pop + loop_invariant: + string: 2147483647LL - (long long )capacity >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 15 + column: 2 + function: pop + loop_invariant: + string: (long long )free >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 15 + column: 2 + function: pop + loop_invariant: + string: (long long )capacity - (long long )free >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 15 + column: 2 + function: pop + loop_invariant: + string: ((0LL - (long long )capacity) + (long long )free) + (long long )used == + 0LL + type: assertion + format: C + +With diff-box: + + $ goblint --enable witness.yaml.enabled --disable witness.invariant.other --disable ana.base.invariant.enabled --set ana.apron.privatization mutex-meet --set ana.activated[+] apron --enable ana.sv-comp.functions --set ana.apron.domain polyhedra --enable ana.apron.invariant.one-var --enable ana.apron.invariant.diff-box 52-queuesize.c + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:67:5-67:31) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:68:5-68:38) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:69:5-69:31) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:70:5-70:38) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:71:5-71:45) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:15:3-15:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:16:3-16:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:17:3-17:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:18:3-18:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:19:3-19:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:26:3-26:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:27:3-27:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:28:3-28:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:29:3-29:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:30:3-30:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:36:3-36:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:37:3-37:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:38:3-38:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:39:3-39:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:40:3-40:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:47:3-47:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:48:3-48:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:49:3-49:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:50:3-50:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:51:3-51:43) + [Warning][Deadcode] Function 'worker' has dead code: + on line 58 (52-queuesize.c:58-58) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 53 + dead: 1 + total: 54 + [Warning][Deadcode][CWE-571] condition '1' is always true (52-queuesize.c:56:10-56:11) + [Warning][Deadcode][CWE-571] condition '1' is always true (52-queuesize.c:78:12-78:13) + [Info][Witness] witness generation summary: + total: 6 + [Info][Race] Memory locations race summary: + safe: 3 + vulnerable: 0 + unsafe: 0 + total: 3 + + $ yamlWitnessStrip < witness.yml | tee witness-enable-diff-box.yml + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 36 + column: 2 + function: push + loop_invariant: + string: (long long )free >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 36 + column: 2 + function: push + loop_invariant: + string: (long long )capacity - (long long )free >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 36 + column: 2 + function: push + loop_invariant: + string: ((0LL - (long long )capacity) + (long long )free) + (long long )used == + 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 15 + column: 2 + function: pop + loop_invariant: + string: (long long )free >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 15 + column: 2 + function: pop + loop_invariant: + string: (long long )capacity - (long long )free >= 0LL + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: 52-queuesize.c + file_hash: $STRIPPED_FILE_HASH + line: 15 + column: 2 + function: pop + loop_invariant: + string: ((0LL - (long long )capacity) + (long long )free) + (long long )used == + 0LL + type: assertion + format: C + +Compare witnesses: + + $ diff witness-disable-diff-box.yml witness-enable-diff-box.yml + 9,19d8 + < string: 2147483647LL - (long long )capacity >= 0LL + < type: assertion + < format: C + < - entry_type: loop_invariant + < location: + < file_name: 52-queuesize.c + < file_hash: $STRIPPED_FILE_HASH + < line: 36 + < column: 2 + < function: push + < loop_invariant: + 44,54d32 + < type: assertion + < format: C + < - entry_type: loop_invariant + < location: + < file_name: 52-queuesize.c + < file_hash: $STRIPPED_FILE_HASH + < line: 15 + < column: 2 + < function: pop + < loop_invariant: + < string: 2147483647LL - (long long )capacity >= 0LL + [1] From 5959095fa4c322e950a4fcccc6a99b222930693c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 17:43:04 +0300 Subject: [PATCH 007/566] Add test for dead code messages (issue #94) Automatic testing wasn't possible in https://github.com/goblint/analyzer/pull/785#issuecomment-1187671949, but is now with cram. --- tests/regression/issue-94.t/issue-94.c | 14 ++++++++++++++ tests/regression/issue-94.t/run.t | 13 +++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/regression/issue-94.t/issue-94.c create mode 100644 tests/regression/issue-94.t/run.t diff --git a/tests/regression/issue-94.t/issue-94.c b/tests/regression/issue-94.t/issue-94.c new file mode 100644 index 0000000000..7bdc65b0a8 --- /dev/null +++ b/tests/regression/issue-94.t/issue-94.c @@ -0,0 +1,14 @@ +#include + +int main() { + int x; + if (1) + x = 1; + else + x = 2; + if (x) + x = 1; + else + x = 2; + assert(x > 1 && x < 0); +} diff --git a/tests/regression/issue-94.t/run.t b/tests/regression/issue-94.t/run.t new file mode 100644 index 0000000000..04c99d3bf2 --- /dev/null +++ b/tests/regression/issue-94.t/run.t @@ -0,0 +1,13 @@ + $ goblint --enable ana.dead-code.lines --enable ana.dead-code.branches issue-94.c + [Error][Assert] Assertion "tmp" will fail. (issue-94.c:13:3-13:35) + [Warning][Deadcode] Function 'main' has dead code: + on line 8 (issue-94.c:8-8) + on line 12 (issue-94.c:12-12) + on line 14 (issue-94.c:14-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 6 + dead: 3 + total: 9 + [Warning][Deadcode][CWE-571] condition '1' is always true (issue-94.c:5:7-5:8) + [Warning][Deadcode][CWE-571] condition 'x' is always true (issue-94.c:9:7-9:8) + [Warning][Deadcode][CWE-570] condition 'x > 1' (possibly inserted by CIL) is always false (issue-94.c:13:3-13:35) From cd90dfe01fd6e4446e10afab0e71f9e4669548a0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 17:54:25 +0300 Subject: [PATCH 008/566] Add test for synthetic YAML witness invariant locations (PR #758) --- tests/regression/pr-758.t/pr-758.c | 22 +++++++++++++++++++++ tests/regression/pr-758.t/run.t | 31 ++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 tests/regression/pr-758.t/pr-758.c create mode 100644 tests/regression/pr-758.t/run.t diff --git a/tests/regression/pr-758.t/pr-758.c b/tests/regression/pr-758.t/pr-758.c new file mode 100644 index 0000000000..87aa41889d --- /dev/null +++ b/tests/regression/pr-758.t/pr-758.c @@ -0,0 +1,22 @@ +// Code from https://github.com/goblint/cil/pull/98 + +int main() { + // for loop + int x = 42; + for (x = 0; x < 10; x++) { // there shouldn't be invariants x <= 9, x <= 10 and 0 <= x before this line + // ... + } + + // expression with side effect + int i, k; + i = k = 0; // there shouldn't be invariant k == 0 before this line + + // compound initializers + struct kala { + int kaal; + int hind; + }; + + struct kala a = {2, 3}; // there shouldn't be invariant a.kaal == 2 before this line + return 0; +} diff --git a/tests/regression/pr-758.t/run.t b/tests/regression/pr-758.t/run.t new file mode 100644 index 0000000000..d7a94e0ff7 --- /dev/null +++ b/tests/regression/pr-758.t/run.t @@ -0,0 +1,31 @@ + $ goblint --enable ana.int.interval --enable witness.yaml.enabled pr-758.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 6 + dead: 0 + total: 6 + [Info][Witness] witness generation summary: + total: 2 + + $ yamlWitnessStrip < witness.yml + - entry_type: loop_invariant + location: + file_name: pr-758.c + file_hash: $STRIPPED_FILE_HASH + line: 21 + column: 2 + function: main + loop_invariant: + string: a.hind == 3 + type: assertion + format: C + - entry_type: loop_invariant + location: + file_name: pr-758.c + file_hash: $STRIPPED_FILE_HASH + line: 20 + column: 14 + function: main + loop_invariant: + string: i == 0 + type: assertion + format: C From 8cc3ff2641831e202447e84805b506a6576b4b92 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Oct 2022 11:35:37 +0300 Subject: [PATCH 009/566] Automate 00-sanity/33-hoare-over-paths using cram --- .../00-sanity/33-hoare-over-paths.c | 4 +- .../00-sanity/33-hoare-over-paths.t | 208 ++++++++++++++++++ 2 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 tests/regression/00-sanity/33-hoare-over-paths.t diff --git a/tests/regression/00-sanity/33-hoare-over-paths.c b/tests/regression/00-sanity/33-hoare-over-paths.c index b30586fa59..4a809685ed 100644 --- a/tests/regression/00-sanity/33-hoare-over-paths.c +++ b/tests/regression/00-sanity/33-hoare-over-paths.c @@ -27,8 +27,8 @@ int main() { // NEW explanation: // using SensitiveDomain correctly keeps both paths as path-sensitivity demands - // TODO: manually check final join node in HTML - // cannot be automated because concrete execution cannot check + // cram test checks internal result + // cannot be automated with annotations because concrete execution cannot check // if _must_ lockset _may_ contain m on some path return 0; } diff --git a/tests/regression/00-sanity/33-hoare-over-paths.t b/tests/regression/00-sanity/33-hoare-over-paths.t new file mode 100644 index 0000000000..4d74277cbc --- /dev/null +++ b/tests/regression/00-sanity/33-hoare-over-paths.t @@ -0,0 +1,208 @@ + $ goblint --set ana.path_sens[+] mutex --set result pretty --set outfile pretty.txt 33-hoare-over-paths.c + [Success][Assert] Assertion "1" will succeed (33-hoare-over-paths.c:11:5-11:24) + [Success][Assert] Assertion "1" will succeed (33-hoare-over-paths.c:16:5-16:24) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total: 7 + + $ cat pretty.txt + Mapping { + 33-hoare-over-paths.c:7:1-34:1(main) -> + {([Unit:(), Unit:(), Unit:(), + Reversed (top or Set (Normal Lvals * booleans)):{}, Unit:(), Unit:(), + top or Set (variables):{}, booleans:False, MT mode:Singlethreaded, + Thread * lifted created and Unit:([main], bot), + value domain * array partitioning deps * Vars with Weak Update * P:(mapping { + Global { + m -> + mutex + } + }, mapping { + }, {}, {}), + top or std * lifted node:(mapping { + }, Unknown node), Unit:()], mapping { + })} + 33-hoare-over-paths.c:9:7-9:8(main) -> + {([Unit:(), Unit:(), Unit:(), + Reversed (top or Set (Normal Lvals * booleans)):{}, Unit:(), Unit:(), + top or Set (variables):{}, booleans:False, MT mode:Singlethreaded, + Thread * lifted created and Unit:([main], bot), + value domain * array partitioning deps * Vars with Weak Update * P:(mapping { + Global { + m -> + mutex + } + Local { + r -> + (Unknown int([-31,31])) + } + }, mapping { + }, {}, {}), + top or std * lifted node:(mapping { + }, Unknown node), Unit:()], mapping { + })} + 33-hoare-over-paths.c:10:5-10:10(main) -> + {([Unit:(), Unit:(), Unit:(), + Reversed (top or Set (Normal Lvals * booleans)):{}, Unit:(), Unit:(), + top or Set (variables):{}, booleans:False, MT mode:Singlethreaded, + Thread * lifted created and Unit:([main], bot), + value domain * array partitioning deps * Vars with Weak Update * P:(mapping { + Global { + m -> + mutex + } + Local { + r -> + (Not {0}([-31,31])) + } + }, mapping { + }, {}, {}), + top or std * lifted node:(mapping { + }, Unknown node), Unit:()], mapping { + })} + 33-hoare-over-paths.c:11:5-11:24(main) -> + {([Unit:(), Unit:(), Unit:(), + Reversed (top or Set (Normal Lvals * booleans)):{}, Unit:(), Unit:(), + top or Set (variables):{}, booleans:False, MT mode:Singlethreaded, + Thread * lifted created and Unit:([main], bot), + value domain * array partitioning deps * Vars with Weak Update * P:(mapping { + Global { + m -> + mutex + } + Local { + r -> + (0) + } + }, mapping { + }, {}, {}), + top or std * lifted node:(mapping { + }, Unknown node), Unit:()], mapping { + })} + 33-hoare-over-paths.c:15:5-15:27(main) -> + {([Unit:(), Unit:(), Unit:(), + Reversed (top or Set (Normal Lvals * booleans)):{}, Unit:(), Unit:(), + top or Set (variables):{}, booleans:False, MT mode:Singlethreaded, + Thread * lifted created and Unit:([main], bot), + value domain * array partitioning deps * Vars with Weak Update * P:(mapping { + Global { + m -> + mutex + } + Local { + r -> + (0) + } + }, mapping { + }, {}, {}), + top or std * lifted node:(mapping { + }, Unknown node), Unit:()], mapping { + })} + 33-hoare-over-paths.c:16:5-16:24(main) -> + {([Unit:(), Unit:(), Unit:(), + Reversed (top or Set (Normal Lvals * booleans)):{m}, Unit:(), Unit:(), + top or Set (variables):{}, booleans:False, MT mode:Singlethreaded, + Thread * lifted created and Unit:([main], bot), + value domain * array partitioning deps * Vars with Weak Update * P:(mapping { + Global { + m -> + mutex + } + Local { + r -> + (0) + } + }, mapping { + }, {}, {}), + top or std * lifted node:(mapping { + }, Unknown node), Unit:()], mapping { + })} + 33-hoare-over-paths.c:33:3-33:11(main) -> + {([Unit:(), Unit:(), Unit:(), + Reversed (top or Set (Normal Lvals * booleans)):{m}, Unit:(), Unit:(), + top or Set (variables):{}, booleans:False, MT mode:Singlethreaded, + Thread * lifted created and Unit:([main], bot), + value domain * array partitioning deps * Vars with Weak Update * P:(mapping { + Global { + m -> + mutex + } + Local { + r -> + (0) + } + }, mapping { + }, {}, {}), + top or std * lifted node:(mapping { + }, Unknown node), Unit:()], mapping { + }), ([Unit:(), + Unit:(), + Unit:(), + Reversed (top or Set (Normal Lvals * booleans)):{}, + Unit:(), + Unit:(), + top or Set (variables):{}, + booleans:False, + MT mode:Singlethreaded, + Thread * lifted created and Unit:([main], bot), + value domain * array partitioning deps * Vars with Weak Update * P:(mapping { + Global { + m -> + mutex + } + Local { + r -> + (0) + } + }, mapping { + }, {}, {}), + top or std * lifted node:(mapping { + }, Unknown node), + Unit:()], mapping { + })} + 33-hoare-over-paths.c:7:1-34:1(main) -> + {([Unit:(), Unit:(), Unit:(), + Reversed (top or Set (Normal Lvals * booleans)):{m}, Unit:(), Unit:(), + top or Set (variables):{}, booleans:False, MT mode:Singlethreaded, + Thread * lifted created and Unit:([main], bot), + value domain * array partitioning deps * Vars with Weak Update * P:(mapping { + Global { + m -> + mutex + } + Temp { + RETURN -> + (0) + } + }, mapping { + }, {}, {}), + top or std * lifted node:(mapping { + }, Unknown node), Unit:()], mapping { + }), ([Unit:(), + Unit:(), + Unit:(), + Reversed (top or Set (Normal Lvals * booleans)):{}, + Unit:(), + Unit:(), + top or Set (variables):{}, + booleans:False, + MT mode:Singlethreaded, + Thread * lifted created and Unit:([main], bot), + value domain * array partitioning deps * Vars with Weak Update * P:(mapping { + Global { + m -> + mutex + } + Temp { + RETURN -> + (0) + } + }, mapping { + }, {}, {}), + top or std * lifted node:(mapping { + }, Unknown node), + Unit:()], mapping { + })} + OTHERS -> Not available + } From 9f5c0e11020bc2ada09373e592626b8b8bb27234 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Oct 2022 12:40:50 +0300 Subject: [PATCH 010/566] Print regression tests without any automatic checks --- scripts/update_suite.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 65070303da..3078d1e7ca 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -312,6 +312,9 @@ def parse_tests (lines) when /TERM/ tests[-1] = "term" end + if tests.empty? then + puts "No automatic checks in #{@id}" + end Tests.new(self, tests, tests_line, todo) end From 4705c301f9487e835d335df4bc9c34fbcf970356 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Oct 2022 12:43:38 +0300 Subject: [PATCH 011/566] Fix many regression tests without automatic checks --- scripts/update_suite.rb | 13 ++++++++++- .../00-sanity/18-parse-empty-array.c | 1 + .../00-sanity/29-earlyglobs-insens.c | 1 + tests/regression/01-cpa/38-enum.c | 2 +- tests/regression/01-cpa/55-def_exc-widen.c | 1 + tests/regression/01-cpa/56-def_exc-fp1.c | 2 +- .../01-cpa/57-def_exc-interval-inconsistent.c | 2 +- tests/regression/02-base/34-builtin_va_list.c | 1 + tests/regression/02-base/64-enums-minmax.c | 1 + .../02-base/69-ipmi-struct-blob-fixpoint.c | 2 +- .../02-base/72-ad-widen-duplicate.c | 1 + .../02-base/73-no-eval-on-write-offset.c | 1 + .../02-base/87-casts-dep-on-param.c | 2 +- .../03-practical/21-pfscan_combine_minimal.c | 1 + .../regression/03-practical/23-knot-timeout.c | 2 +- tests/regression/06-symbeq/40-var_eq-widen1.c | 2 +- .../10-synch/23-tid-partitioned-array.c | 1 + .../24-tid-partitioned-array-global.c | 1 + .../regression/10-synch/25-tid-array-malloc.c | 1 + .../10-synch/26-tid-array-malloc-free.c | 1 + .../59-smtprc_threadenter_path_minimal.c | 2 +- tests/regression/18-file/01-ok.c | 10 ++++----- tests/regression/18-file/02-function.c | 10 ++++----- tests/regression/18-file/08-var-reuse.c | 16 +++++++------- tests/regression/18-file/10-inf-loop-ok.c | 10 ++++----- tests/regression/18-file/25-mem-ok.c | 22 +++++++++---------- tests/regression/18-file/26-open-error-ok.c | 10 ++++----- tests/regression/18-file/29-alias-global.c | 14 ++++++------ tests/regression/18-file/36-fun-ptr.c | 10 ++++----- .../20-slr_term/06-trylock_rc_slr.c | 1 + .../29-svcomp/05-isp1362-malloc-fun.c | 2 +- .../regression/29-svcomp/11-arithmetic-bot.c | 1 + tests/regression/29-svcomp/12-interval-bot.c | 2 +- .../regression/29-svcomp/13-comparision-bot.c | 1 + .../14-addition-in-comparision-bot.c | 1 + tests/regression/29-svcomp/19-problematic.c | 1 + tests/regression/29-svcomp/21-issue-casting.c | 1 + .../regression/31-ikind-aware-ints/03-lnot.c | 1 + .../31-ikind-aware-ints/04-ptrdiff.c | 1 + .../regression/31-ikind-aware-ints/05-shift.c | 2 +- .../31-ikind-aware-ints/06-structs.c | 1 + .../31-ikind-aware-ints/15-strange.c | 2 +- .../31-ikind-aware-ints/17-def-enum-refine.c | 2 +- .../35-marshaling/01-disable_hashcons.c | 1 + .../54-unroll_arrays/03-large_index_type.c | 4 ++-- .../54-unroll_arrays/04-access_no_bounds.c | 4 ++-- .../56-witness/50-witness-lifter-fp1.c | 2 +- 47 files changed, 103 insertions(+), 70 deletions(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 3078d1e7ca..bbce8614a2 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -203,6 +203,8 @@ def compare_warnings check.call warnings[idx] != "race" when "nodeadlock" check.call warnings[idx] != "deadlock" + when "nocrash", "fixpoint", "notimeout", "cram" + check.call true end end end @@ -281,6 +283,15 @@ def parse_tests (lines) if obj =~ /#line ([0-9]+).*$/ then i = $1.to_i - 1 end + if obj =~ /NOCRASH/ then + tests[-42] = "nocrash" + elsif obj =~ /FIXPOINT/ then + tests[-42] = "fixpoint" + elsif obj =~ /NOTIMEOUT/ then + tests[-42] = "notimeout" + elsif obj =~ /CRAM/ then + tests[-42] = "cram" + end next if obj =~ /^\s*\/\// || obj =~ /^\s*\/\*([^*]|\*+[^*\/])*\*\/$/ todo << i if obj =~ /TODO|SKIP/ tests_line[i] = obj @@ -313,7 +324,7 @@ def parse_tests (lines) tests[-1] = "term" end if tests.empty? then - puts "No automatic checks in #{@id}" + puts "No automatic checks in #{@id} (maybe NOCRASH/FIXPOINT/NOTIMEOUT/CRAM?)" end Tests.new(self, tests, tests_line, todo) end diff --git a/tests/regression/00-sanity/18-parse-empty-array.c b/tests/regression/00-sanity/18-parse-empty-array.c index a38cbc3712..2205d412da 100644 --- a/tests/regression/00-sanity/18-parse-empty-array.c +++ b/tests/regression/00-sanity/18-parse-empty-array.c @@ -1,4 +1,5 @@ // Test for issue in https://github.com/goblint/cil/issues/19 +// NOCRASH static int a[]; static int a[] = {}; static int b[0] = {}; diff --git a/tests/regression/00-sanity/29-earlyglobs-insens.c b/tests/regression/00-sanity/29-earlyglobs-insens.c index 9d2f186c3e..e671522c51 100644 --- a/tests/regression/00-sanity/29-earlyglobs-insens.c +++ b/tests/regression/00-sanity/29-earlyglobs-insens.c @@ -1,2 +1,3 @@ // PARAM: --set ana.ctx_insens[+] base --enable exp.earlyglobs +// NOCRASH int main() {} diff --git a/tests/regression/01-cpa/38-enum.c b/tests/regression/01-cpa/38-enum.c index 818705843e..ff345b6ba7 100644 --- a/tests/regression/01-cpa/38-enum.c +++ b/tests/regression/01-cpa/38-enum.c @@ -1,7 +1,7 @@ // PARAM: --disable ana.int.interval --disable ana.int.def_exc --enable ana.int.enums void main(){ int n = 1; - for (; n; n++) { // fixed point not reached here + for (; n; n++) { // FIXPOINT: previously fixed point not reached here } return; } diff --git a/tests/regression/01-cpa/55-def_exc-widen.c b/tests/regression/01-cpa/55-def_exc-widen.c index 23b724ae7b..c19f3ab62e 100644 --- a/tests/regression/01-cpa/55-def_exc-widen.c +++ b/tests/regression/01-cpa/55-def_exc-widen.c @@ -1,4 +1,5 @@ //PARAM: --disable ana.int.def_exc_widen_by_join +// NOTIMEOUT int main(int argc , char **argv ) { char buf[512]; diff --git a/tests/regression/01-cpa/56-def_exc-fp1.c b/tests/regression/01-cpa/56-def_exc-fp1.c index 69390b1e35..8e38a04531 100644 --- a/tests/regression/01-cpa/56-def_exc-fp1.c +++ b/tests/regression/01-cpa/56-def_exc-fp1.c @@ -1,6 +1,6 @@ // PARAM: --enable ana.sv-comp.functions // manually minimized from sv-benchmarks/c/seq-mthreaded/pals_lcr.3.ufo.UNBOUNDED.pals.c -// used to not reach fixpoint due to def_exc range +// FIXPOINT: used to not reach fixpoint due to def_exc range _Bool __VERIFIER_nondet_bool(void) ; _Bool mode1 ; diff --git a/tests/regression/01-cpa/57-def_exc-interval-inconsistent.c b/tests/regression/01-cpa/57-def_exc-interval-inconsistent.c index a550ea172f..b0ac9ae5ba 100644 --- a/tests/regression/01-cpa/57-def_exc-interval-inconsistent.c +++ b/tests/regression/01-cpa/57-def_exc-interval-inconsistent.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.def_exc --enable ana.int.interval --enable ana.sv-comp.functions --set sem.int.signed_overflow assume_none --set ana.int.refinement never -// used to crash in branch when is_bool returned true, but to_bool returned None on (0,[1,1]) +// NOCRASH: used to crash in branch when is_bool returned true, but to_bool returned None on (0,[1,1]) // manually minimized from sv-benchmarks/c/recursive/MultCommutative-2.c extern int __VERIFIER_nondet_int(void); diff --git a/tests/regression/02-base/34-builtin_va_list.c b/tests/regression/02-base/34-builtin_va_list.c index 2ab9168c68..f16d6fc0fd 100644 --- a/tests/regression/02-base/34-builtin_va_list.c +++ b/tests/regression/02-base/34-builtin_va_list.c @@ -1,3 +1,4 @@ +// NOCRASH #include int sum(int n, ...) { diff --git a/tests/regression/02-base/64-enums-minmax.c b/tests/regression/02-base/64-enums-minmax.c index 9d366aa56f..6c0d827cf0 100644 --- a/tests/regression/02-base/64-enums-minmax.c +++ b/tests/regression/02-base/64-enums-minmax.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.enums +// NOTIMEOUT int main(void) { unsigned char c; int top; diff --git a/tests/regression/02-base/69-ipmi-struct-blob-fixpoint.c b/tests/regression/02-base/69-ipmi-struct-blob-fixpoint.c index 3aa06d1820..8e7e42f0e6 100644 --- a/tests/regression/02-base/69-ipmi-struct-blob-fixpoint.c +++ b/tests/regression/02-base/69-ipmi-struct-blob-fixpoint.c @@ -30,7 +30,7 @@ static int ipmi_open(struct inode *inode, struct file *file) struct ipmi_file_private *priv; priv = kmalloc(sizeof(*priv), GFP_KERNEL); mutex_lock(&ipmi_mutex); - priv->file = file; // should reach fixpoint from priv side effect from here + priv->file = file; // FIXPOINT: should reach fixpoint from priv side effect from here ipmi_create_user(0, &ipmi_hndlrs, priv, &(priv->user)); file->private_data = priv; diff --git a/tests/regression/02-base/72-ad-widen-duplicate.c b/tests/regression/02-base/72-ad-widen-duplicate.c index 436ae70c93..c893d4ae79 100644 --- a/tests/regression/02-base/72-ad-widen-duplicate.c +++ b/tests/regression/02-base/72-ad-widen-duplicate.c @@ -1,4 +1,5 @@ // https://github.com/goblint/analyzer/issues/554 +// FIXPOINT int a; int main() { c(&a); } diff --git a/tests/regression/02-base/73-no-eval-on-write-offset.c b/tests/regression/02-base/73-no-eval-on-write-offset.c index da0e635dfb..780c244e9a 100644 --- a/tests/regression/02-base/73-no-eval-on-write-offset.c +++ b/tests/regression/02-base/73-no-eval-on-write-offset.c @@ -1,4 +1,5 @@ // PARAM: --enable exp.earlyglobs +// NOCRASH char a; int c; diff --git a/tests/regression/02-base/87-casts-dep-on-param.c b/tests/regression/02-base/87-casts-dep-on-param.c index 93a16e4bc3..a265b9db04 100644 --- a/tests/regression/02-base/87-casts-dep-on-param.c +++ b/tests/regression/02-base/87-casts-dep-on-param.c @@ -9,7 +9,7 @@ int g () { int a[10]; f(1, &i); - // check that i is an integer and can be used for array indexing without Goblint crashing + // NOCRASH: check that i is an integer and can be used for array indexing without Goblint crashing int r = a[i]; a[i] = r; diff --git a/tests/regression/03-practical/21-pfscan_combine_minimal.c b/tests/regression/03-practical/21-pfscan_combine_minimal.c index 5054260216..abdef0627b 100644 --- a/tests/regression/03-practical/21-pfscan_combine_minimal.c +++ b/tests/regression/03-practical/21-pfscan_combine_minimal.c @@ -1,3 +1,4 @@ +// FIXPOINT #include struct __anonstruct_PQUEUE_63 { diff --git a/tests/regression/03-practical/23-knot-timeout.c b/tests/regression/03-practical/23-knot-timeout.c index f972363cea..2fafe5d4da 100644 --- a/tests/regression/03-practical/23-knot-timeout.c +++ b/tests/regression/03-practical/23-knot-timeout.c @@ -1,5 +1,5 @@ // PARAM: --disable ana.int.def_exc_widen_by_join -// Used to timeout without ana.int.def_exc_widen_by_join +// NOTIMEOUT: Used to timeout without ana.int.def_exc_widen_by_join #include struct a { diff --git a/tests/regression/06-symbeq/40-var_eq-widen1.c b/tests/regression/06-symbeq/40-var_eq-widen1.c index 72e28c57a0..1cef7fe324 100644 --- a/tests/regression/06-symbeq/40-var_eq-widen1.c +++ b/tests/regression/06-symbeq/40-var_eq-widen1.c @@ -1,6 +1,6 @@ // PARAM: --set ana.activated[+] var_eq // manually minimized from sv-benchmarks/c/ldv-linux-3.16-rc1/205_9a_array_unsafes_linux-3.16-rc1.tar.xz-205_9a-drivers--net--usb--cx82310_eth.ko-entry_point.cil.out.i -// used to call widen incorrectly +// NOCRASH: used to call widen incorrectly typedef _Bool bool; void usb_bulk_msg(int *arg4, int x) { diff --git a/tests/regression/10-synch/23-tid-partitioned-array.c b/tests/regression/10-synch/23-tid-partitioned-array.c index 0f4942ec0e..e0dc849fad 100644 --- a/tests/regression/10-synch/23-tid-partitioned-array.c +++ b/tests/regression/10-synch/23-tid-partitioned-array.c @@ -1,4 +1,5 @@ // PARAM: --set ana.activated[+] thread --set ana.base.arrays.domain partitioned +// NOCRASH #include void *t_fun(void *arg) { diff --git a/tests/regression/10-synch/24-tid-partitioned-array-global.c b/tests/regression/10-synch/24-tid-partitioned-array-global.c index b61daed77f..00685dc1c8 100644 --- a/tests/regression/10-synch/24-tid-partitioned-array-global.c +++ b/tests/regression/10-synch/24-tid-partitioned-array-global.c @@ -1,4 +1,5 @@ // PARAM: --set ana.activated[+] thread --set ana.base.arrays.domain partitioned +// NOCRASH #include pthread_t t_ids[10000]; diff --git a/tests/regression/10-synch/25-tid-array-malloc.c b/tests/regression/10-synch/25-tid-array-malloc.c index 4000577fd3..e59cd02e97 100644 --- a/tests/regression/10-synch/25-tid-array-malloc.c +++ b/tests/regression/10-synch/25-tid-array-malloc.c @@ -1,4 +1,5 @@ // PARAM: --set ana.activated[+] thread +// NOCRASH #include #include diff --git a/tests/regression/10-synch/26-tid-array-malloc-free.c b/tests/regression/10-synch/26-tid-array-malloc-free.c index e8f00d13ae..b070fd0d84 100644 --- a/tests/regression/10-synch/26-tid-array-malloc-free.c +++ b/tests/regression/10-synch/26-tid-array-malloc-free.c @@ -1,4 +1,5 @@ // PARAM: --set ana.activated[+] thread +// NOCRASH #include #include diff --git a/tests/regression/13-privatized/59-smtprc_threadenter_path_minimal.c b/tests/regression/13-privatized/59-smtprc_threadenter_path_minimal.c index 5a9342cfd0..dcbc617e0b 100644 --- a/tests/regression/13-privatized/59-smtprc_threadenter_path_minimal.c +++ b/tests/regression/13-privatized/59-smtprc_threadenter_path_minimal.c @@ -48,7 +48,7 @@ int main(int argc , char **argv ) } pthread_mutex_unlock(& main_thread_count_mutex); - // lock gets here with two paths and crashes + // NOCRASH: lock gets here with two paths and crashes pthread_create(& c_tid, NULL, & thread_start, NULL); return (0); } \ No newline at end of file diff --git a/tests/regression/18-file/01-ok.c b/tests/regression/18-file/01-ok.c index 5c1f21ff1c..db8595022a 100644 --- a/tests/regression/18-file/01-ok.c +++ b/tests/regression/18-file/01-ok.c @@ -1,12 +1,12 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic +// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic --disable warn.info #include int main(){ FILE *fp; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); -} + fp = fopen("test.txt", "a"); // NOWARN + fprintf(fp, "Testing...\n"); // NOWARN + fclose(fp); // NOWARN +} // NOWARN // All ok! diff --git a/tests/regression/18-file/02-function.c b/tests/regression/18-file/02-function.c index fc3157c264..eb869b2de7 100644 --- a/tests/regression/18-file/02-function.c +++ b/tests/regression/18-file/02-function.c @@ -1,17 +1,17 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic +// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic --disable warn.info #include FILE *fp; void f(){ - fp = fopen("test.txt", "a"); + fp = fopen("test.txt", "a"); // NOWARN } int main(){ f(); - fprintf(fp, "Testing...\n"); - fclose(fp); -} + fprintf(fp, "Testing...\n"); // NOWARN + fclose(fp); // NOWARN +} // NOWARN // All ok! diff --git a/tests/regression/18-file/08-var-reuse.c b/tests/regression/18-file/08-var-reuse.c index 1caa238517..001e3592ad 100644 --- a/tests/regression/18-file/08-var-reuse.c +++ b/tests/regression/18-file/08-var-reuse.c @@ -1,15 +1,15 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic +// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic --disable warn.info #include int main(){ FILE *fp; - fp = fopen("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); - fp = fopen("test2.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); -} + fp = fopen("test.txt", "a"); // NOWARN + fprintf(fp, "Testing...\n"); // NOWARN + fclose(fp); // NOWARN + fp = fopen("test2.txt", "a"); // NOWARN + fprintf(fp, "Testing...\n"); // NOWARN + fclose(fp); // NOWARN +} // NOWARN // All ok! diff --git a/tests/regression/18-file/10-inf-loop-ok.c b/tests/regression/18-file/10-inf-loop-ok.c index d88fde272e..51ea8e8845 100644 --- a/tests/regression/18-file/10-inf-loop-ok.c +++ b/tests/regression/18-file/10-inf-loop-ok.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic +// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic --disable warn.info #include @@ -6,14 +6,14 @@ FILE *fp; int main(){ int i; - fp = fopen("test.txt", "a"); + fp = fopen("test.txt", "a"); // NOWARN while (i){ - fprintf(fp, "Testing...\n"); + fprintf(fp, "Testing...\n"); // NOWARN i++; } - fclose(fp); -} + fclose(fp); // NOWARN +} // NOWARN // All ok. diff --git a/tests/regression/18-file/25-mem-ok.c b/tests/regression/18-file/25-mem-ok.c index 00ba189b8d..becda78caa 100644 --- a/tests/regression/18-file/25-mem-ok.c +++ b/tests/regression/18-file/25-mem-ok.c @@ -1,13 +1,13 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic +// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic --disable warn.info #include int main(){ FILE *fp[3]; // Array -> varinfo with index-offset - fp[1] = fopen("test.txt", "a"); - fprintf(fp[1], "Testing...\n"); - fclose(fp[1]); + fp[1] = fopen("test.txt", "a"); // NOWARN + fprintf(fp[1], "Testing...\n"); // NOWARN + fclose(fp[1]); // NOWARN struct foo { @@ -15,15 +15,15 @@ int main(){ FILE *fp; } bar; // Struct -> varinfo with field-offset - bar.fp = fopen("test.txt", "a"); - fprintf(bar.fp, "Testing...\n"); - fclose(bar.fp); + bar.fp = fopen("test.txt", "a"); // NOWARN + fprintf(bar.fp, "Testing...\n"); // NOWARN + fclose(bar.fp); // NOWARN // Pointer -> Mem exp - *(fp+2) = fopen("test.txt", "a"); - fprintf(*(fp+2), "Testing...\n"); - fclose(*(fp+2)); -} + *(fp+2) = fopen("test.txt", "a"); // NOWARN + fprintf(*(fp+2), "Testing...\n"); // NOWARN + fclose(*(fp+2)); // NOWARN +} // NOWARN // All ok! diff --git a/tests/regression/18-file/26-open-error-ok.c b/tests/regression/18-file/26-open-error-ok.c index 5cf3aaf7bb..45fc907cb7 100644 --- a/tests/regression/18-file/26-open-error-ok.c +++ b/tests/regression/18-file/26-open-error-ok.c @@ -1,15 +1,15 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic +// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic --disable warn.info #include int main (){ FILE *fp; - fp = fopen("test.txt", "w"); + fp = fopen("test.txt", "w"); // NOWARN if(fp!=NULL){ - fprintf(fp, "Testing..."); - fclose(fp); + fprintf(fp, "Testing..."); // NOWARN + fclose(fp); // NOWARN } -} +} // NOWARN // All ok! diff --git a/tests/regression/18-file/29-alias-global.c b/tests/regression/18-file/29-alias-global.c index 17b94748c0..4e28393bbc 100644 --- a/tests/regression/18-file/29-alias-global.c +++ b/tests/regression/18-file/29-alias-global.c @@ -1,10 +1,10 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic +// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic --disable warn.info #include FILE* fp; FILE* myfopen(char* f){ - fp = fopen(f, "a"); + fp = fopen(f, "a"); // NOWARN return fp; } @@ -13,10 +13,10 @@ int main(){ FILE *fp2; fp1 = myfopen("test1.txt"); fp2 = myfopen("test2.txt"); - fprintf(fp1, "Testing...\n"); - fclose(fp1); - fprintf(fp2, "Testing...\n"); - fclose(fp2); -} + fprintf(fp1, "Testing...\n"); // NOWARN + fclose(fp1); // NOWARN + fprintf(fp2, "Testing...\n"); // NOWARN + fclose(fp2); // NOWARN +} // NOWARN // All ok! diff --git a/tests/regression/18-file/36-fun-ptr.c b/tests/regression/18-file/36-fun-ptr.c index 4f70bf7382..4e50f560fd 100644 --- a/tests/regression/18-file/36-fun-ptr.c +++ b/tests/regression/18-file/36-fun-ptr.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic +// PARAM: --set ana.activated[+] "'file'" --enable ana.file.optimistic --disable warn.info #include @@ -6,9 +6,9 @@ int main(){ FILE *fp; FILE* (*f)(const char *, const char*); f = fopen; - fp = f("test.txt", "a"); - fprintf(fp, "Testing...\n"); - fclose(fp); -} + fp = f("test.txt", "a"); // NOWARN + fprintf(fp, "Testing...\n"); // NOWARN + fclose(fp); // NOWARN +} // NOWARN // All ok! diff --git a/tests/regression/20-slr_term/06-trylock_rc_slr.c b/tests/regression/20-slr_term/06-trylock_rc_slr.c index ab24b142f6..d6e3c3f31e 100644 --- a/tests/regression/20-slr_term/06-trylock_rc_slr.c +++ b/tests/regression/20-slr_term/06-trylock_rc_slr.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --set solver slr3t +// FIXPOINT #include pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/tests/regression/29-svcomp/05-isp1362-malloc-fun.c b/tests/regression/29-svcomp/05-isp1362-malloc-fun.c index b8ded7e341..f1c8dece14 100644 --- a/tests/regression/29-svcomp/05-isp1362-malloc-fun.c +++ b/tests/regression/29-svcomp/05-isp1362-malloc-fun.c @@ -1,5 +1,5 @@ // PARAM: --set ana.malloc.wrappers "['ldv_malloc']" - +// NOCRASH #include typedef unsigned long __kernel_ulong_t; diff --git a/tests/regression/29-svcomp/11-arithmetic-bot.c b/tests/regression/29-svcomp/11-arithmetic-bot.c index ce8635b939..d365f14a31 100644 --- a/tests/regression/29-svcomp/11-arithmetic-bot.c +++ b/tests/regression/29-svcomp/11-arithmetic-bot.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval --enable ana.int.def_exc // from: ldv-linux-3.0/usb_urb-drivers-vhost-vhost_net.ko.cil.out.i +// NOCRASH typedef unsigned long long u64; int main( ) diff --git a/tests/regression/29-svcomp/12-interval-bot.c b/tests/regression/29-svcomp/12-interval-bot.c index 77739efdbd..9d6157875a 100644 --- a/tests/regression/29-svcomp/12-interval-bot.c +++ b/tests/regression/29-svcomp/12-interval-bot.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.interval --enable ana.int.def_exc - +// NOCRASH int main(){ unsigned long long a ; diff --git a/tests/regression/29-svcomp/13-comparision-bot.c b/tests/regression/29-svcomp/13-comparision-bot.c index f64fa8a349..843a5fabb4 100644 --- a/tests/regression/29-svcomp/13-comparision-bot.c +++ b/tests/regression/29-svcomp/13-comparision-bot.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --enable ana.int.def_exc +// NOCRASH #include int main(){ int a = 0; diff --git a/tests/regression/29-svcomp/14-addition-in-comparision-bot.c b/tests/regression/29-svcomp/14-addition-in-comparision-bot.c index f667ca88f6..c729cf6019 100644 --- a/tests/regression/29-svcomp/14-addition-in-comparision-bot.c +++ b/tests/regression/29-svcomp/14-addition-in-comparision-bot.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval +// NOCRASH int main() { unsigned int top; diff --git a/tests/regression/29-svcomp/19-problematic.c b/tests/regression/29-svcomp/19-problematic.c index 63b4fc7af0..99c7c01197 100644 --- a/tests/regression/29-svcomp/19-problematic.c +++ b/tests/regression/29-svcomp/19-problematic.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.sv-comp.functions // Adapted from: https://github.com/sosy-lab/sv-benchmarks/blob/master/c/ldv-regression/test27-2.c +// NOTIMEOUT extern int __VERIFIER_nondet_int(void); struct dummy { diff --git a/tests/regression/29-svcomp/21-issue-casting.c b/tests/regression/29-svcomp/21-issue-casting.c index e02989559c..35d81d4898 100644 --- a/tests/regression/29-svcomp/21-issue-casting.c +++ b/tests/regression/29-svcomp/21-issue-casting.c @@ -1,6 +1,7 @@ // PARAM: --set ana.activated ["'base'","'mallocWrapper'"] --set ana.base.privatization none // minimal analyses to reveal bug // none privatization because mutex deactivated +// NOTIMEOUT static long main(void) { unsigned int cmd; diff --git a/tests/regression/31-ikind-aware-ints/03-lnot.c b/tests/regression/31-ikind-aware-ints/03-lnot.c index 2fb6d79c61..6d96d88dea 100644 --- a/tests/regression/31-ikind-aware-ints/03-lnot.c +++ b/tests/regression/31-ikind-aware-ints/03-lnot.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval +// NOCRASH int main() { unsigned int l = 0; diff --git a/tests/regression/31-ikind-aware-ints/04-ptrdiff.c b/tests/regression/31-ikind-aware-ints/04-ptrdiff.c index 4be48c7793..be658b3cd0 100644 --- a/tests/regression/31-ikind-aware-ints/04-ptrdiff.c +++ b/tests/regression/31-ikind-aware-ints/04-ptrdiff.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] var_eq +// NOCRASH int *tmp; int main () diff --git a/tests/regression/31-ikind-aware-ints/05-shift.c b/tests/regression/31-ikind-aware-ints/05-shift.c index a0cf2182d8..87bfba5a0b 100644 --- a/tests/regression/31-ikind-aware-ints/05-shift.c +++ b/tests/regression/31-ikind-aware-ints/05-shift.c @@ -1,6 +1,6 @@ // PARAM: --enable ana.int.interval int main(void) { - // Shifting by a negative number is UB, but we should still not crash on it, but go to top instead + // NOCRASH: Shifting by a negative number is UB, but we should still not crash on it, but go to top instead int v = -1; int r = 17; int u = r >> v; diff --git a/tests/regression/31-ikind-aware-ints/06-structs.c b/tests/regression/31-ikind-aware-ints/06-structs.c index ddf3b51cf0..d3c944c4cd 100644 --- a/tests/regression/31-ikind-aware-ints/06-structs.c +++ b/tests/regression/31-ikind-aware-ints/06-structs.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval +// NOCRASH struct rtl8169_private { unsigned int features ; }; diff --git a/tests/regression/31-ikind-aware-ints/15-strange.c b/tests/regression/31-ikind-aware-ints/15-strange.c index d063b3c6e2..550baf0e1d 100644 --- a/tests/regression/31-ikind-aware-ints/15-strange.c +++ b/tests/regression/31-ikind-aware-ints/15-strange.c @@ -1,7 +1,7 @@ //PARAM: --disable ana.int.interval --disable ana.int.def_exc --enable ana.int.enums int main (int argc, char* argv[]) { - // This used to cause an exception because of incompatible ikinds + // NOCRASH: This used to cause an exception because of incompatible ikinds // See https://github.com/goblint/cil/issues/29 signed char f2 = 7; signed char l_1857 = ((0xFFBD4A17L && f2) | f2); diff --git a/tests/regression/31-ikind-aware-ints/17-def-enum-refine.c b/tests/regression/31-ikind-aware-ints/17-def-enum-refine.c index e34d2d1c79..fd3a75154e 100644 --- a/tests/regression/31-ikind-aware-ints/17-def-enum-refine.c +++ b/tests/regression/31-ikind-aware-ints/17-def-enum-refine.c @@ -4,7 +4,7 @@ int main() { _Bool c; if(c) { x--;} else { x--;} - // The veryfier claimed that the fixed-point was not reached here due to a bug in Enums.leq + // FIXPOINT: The veryfier claimed that the fixed-point was not reached here due to a bug in Enums.leq // The leq wrongly returned false for the Enums {0} and not{}[0,1] return 0; } diff --git a/tests/regression/35-marshaling/01-disable_hashcons.c b/tests/regression/35-marshaling/01-disable_hashcons.c index f6a72bd685..5be9e47819 100644 --- a/tests/regression/35-marshaling/01-disable_hashcons.c +++ b/tests/regression/35-marshaling/01-disable_hashcons.c @@ -1,2 +1,3 @@ // PARAM: --disable ana.opt.hashcons +// NOCRASH int main(void) { return 0; } diff --git a/tests/regression/54-unroll_arrays/03-large_index_type.c b/tests/regression/54-unroll_arrays/03-large_index_type.c index d2e902be83..80851aa4b8 100644 --- a/tests/regression/54-unroll_arrays/03-large_index_type.c +++ b/tests/regression/54-unroll_arrays/03-large_index_type.c @@ -1,9 +1,9 @@ //PARAM: --set ana.base.arrays.domain unroll --set ana.base.arrays.unrolling-factor 5 //from sv-comp test c/aws-c-common/memset_using_uint64_harness.i - +// NOCRASH typedef long unsigned int size_t; -int main() { +int main() { int d[]; size_t num_uint64s; diff --git a/tests/regression/54-unroll_arrays/04-access_no_bounds.c b/tests/regression/54-unroll_arrays/04-access_no_bounds.c index d0a5634b53..fe4886962e 100644 --- a/tests/regression/54-unroll_arrays/04-access_no_bounds.c +++ b/tests/regression/54-unroll_arrays/04-access_no_bounds.c @@ -1,6 +1,6 @@ // PARAM: --set ana.base.arrays.domain unroll --set ana.base.arrays.unrolling-factor 5 //from sv-comp test c/aws-c-common/aws_array_eq_c_str_ignore_case_harness.i - +// NOCRASH typedef long unsigned int size_t; typedef unsigned char uint8_t; @@ -18,7 +18,7 @@ static const uint8_t s_tolower_table[256] = { 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}; -int main() { +int main() { size_t array_len = 10; void *array = malloc(array_len); diff --git a/tests/regression/56-witness/50-witness-lifter-fp1.c b/tests/regression/56-witness/50-witness-lifter-fp1.c index 8c6e45e648..1db839cd7b 100644 --- a/tests/regression/56-witness/50-witness-lifter-fp1.c +++ b/tests/regression/56-witness/50-witness-lifter-fp1.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --set ana.specification 'CHECK( init(main()), LTL(G ! call(reach_error())) )' --enable ana.int.interval -// previously fixpoint not reached +// FIXPOINT: previously fixpoint not reached // extracted from sv-benchmarks loops-crafted-1/loopv2 int SIZE = 50000001; int __VERIFIER_nondet_int(); From f1ca09d68762c4453cef00f6211d504d370feb18 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Oct 2022 12:53:51 +0300 Subject: [PATCH 012/566] Add cram tests for manual checks --- .../63-access-threadspawn-lval.c | 1 + .../63-access-threadspawn-lval.t | 25 +++++++++++++++++++ .../13-privatized/64-access-invalidate.c | 1 + .../13-privatized/64-access-invalidate.t | 22 ++++++++++++++++ tests/regression/41-stdlib/03-noqsort.t | 8 ++++++ tests/regression/41-stdlib/dune | 2 ++ 6 files changed, 59 insertions(+) create mode 100644 tests/regression/13-privatized/63-access-threadspawn-lval.t create mode 100644 tests/regression/13-privatized/64-access-invalidate.t create mode 100644 tests/regression/41-stdlib/03-noqsort.t create mode 100644 tests/regression/41-stdlib/dune diff --git a/tests/regression/13-privatized/63-access-threadspawn-lval.c b/tests/regression/13-privatized/63-access-threadspawn-lval.c index 7f98b129ab..9ebd29a89d 100644 --- a/tests/regression/13-privatized/63-access-threadspawn-lval.c +++ b/tests/regression/13-privatized/63-access-threadspawn-lval.c @@ -1,3 +1,4 @@ +// CRAM #include pthread_t id1; diff --git a/tests/regression/13-privatized/63-access-threadspawn-lval.t b/tests/regression/13-privatized/63-access-threadspawn-lval.t new file mode 100644 index 0000000000..8639d69fe3 --- /dev/null +++ b/tests/regression/13-privatized/63-access-threadspawn-lval.t @@ -0,0 +1,25 @@ +Should have (safe) write accesses to id1 and id2: + + $ goblint --enable allglobs 63-access-threadspawn-lval.c + [Error][Imprecise][Unsound] Function definition missing for magic2 (63-access-threadspawn-lval.c:21:3-21:12) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (63-access-threadspawn-lval.c:21:3-21:12) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(A, + NoOffset)), AddrOf(Var(id2, NoOffset)), AddrOf(Var(id1, NoOffset)), AddrOf(Var(e, NoOffset)) (63-access-threadspawn-lval.c:21:3-21:12) + [Error][Imprecise][Unsound] Function definition missing for magic1 (63-access-threadspawn-lval.c:13:3-13:11) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (63-access-threadspawn-lval.c:13:3-13:11) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(A, NoOffset)), AddrOf(Var(id2, NoOffset)), AddrOf(Var(id1, NoOffset)) (63-access-threadspawn-lval.c:13:3-13:11) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 13 + dead: 0 + total: 13 + [Success][Race] Memory location id1@63-access-threadspawn-lval.c:4:11-4:14 (safe): + write with [mhp:{tid=[main]}, + multi:false, thread:[main]] (conf. 110) (63-access-threadspawn-lval.c:27:3-27:37) + [Success][Race] Memory location id2@63-access-threadspawn-lval.c:5:11-5:14 (safe): + write with [mhp:{tid=[main]; created={[main, f@63-access-threadspawn-lval.c:27:3-27:37]}}, thread:[main]] (conf. 110) (63-access-threadspawn-lval.c:28:3-28:37) + write with [mhp:{tid=[main]; created={[main, f@63-access-threadspawn-lval.c:27:3-27:37]}}, thread:[main]] (conf. 110) (63-access-threadspawn-lval.c:28:3-28:37) + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total: 2 diff --git a/tests/regression/13-privatized/64-access-invalidate.c b/tests/regression/13-privatized/64-access-invalidate.c index 7a191a9650..1461d413e2 100644 --- a/tests/regression/13-privatized/64-access-invalidate.c +++ b/tests/regression/13-privatized/64-access-invalidate.c @@ -1,3 +1,4 @@ +// CRAM #include pthread_t id; diff --git a/tests/regression/13-privatized/64-access-invalidate.t b/tests/regression/13-privatized/64-access-invalidate.t new file mode 100644 index 0000000000..94613b1bbf --- /dev/null +++ b/tests/regression/13-privatized/64-access-invalidate.t @@ -0,0 +1,22 @@ +Should have (safe) write access to id1 and magic2 invalidate to A: + + $ goblint --enable allglobs 64-access-invalidate.c + [Error][Imprecise][Unsound] Function definition missing for magic2 (64-access-invalidate.c:16:3-16:12) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (64-access-invalidate.c:16:3-16:12) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(A, + NoOffset)), AddrOf(Var(id, NoOffset)), AddrOf(Var(e, NoOffset)) (64-access-invalidate.c:16:3-16:12) + [Error][Imprecise][Unsound] Function definition missing for magic1 (64-access-invalidate.c:12:3-12:11) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (64-access-invalidate.c:12:3-12:11) + [Info][Imprecise] Invalidating expressions: AddrOf(Var(A, NoOffset)), AddrOf(Var(id, NoOffset)) (64-access-invalidate.c:12:3-12:11) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 10 + dead: 0 + total: 10 + [Success][Race] Memory location id@64-access-invalidate.c:4:11-4:13 (safe): + write with [mhp:{tid=[main]}, + multi:false, thread:[main]] (conf. 110) (64-access-invalidate.c:21:3-21:36) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total: 1 diff --git a/tests/regression/41-stdlib/03-noqsort.t b/tests/regression/41-stdlib/03-noqsort.t new file mode 100644 index 0000000000..689906659f --- /dev/null +++ b/tests/regression/41-stdlib/03-noqsort.t @@ -0,0 +1,8 @@ +There should be no CIL warning about multiple definitions: + + $ goblint --set pre.cppflags[+] -DGOBLINT_NO_QSORT 03-noqsort.c + [Warning][Deadcode] Function 'qsort' is uncalled: 1 LLoC (03-noqsort.c:5:1-6:1) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 2 + dead: 1 (1 in uncalled functions) + total: 3 diff --git a/tests/regression/41-stdlib/dune b/tests/regression/41-stdlib/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/41-stdlib/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) From 7b3a3ec96299f3cd4dbe287b8391028cbcfeb652 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Oct 2022 13:40:19 +0300 Subject: [PATCH 013/566] Hide getdate_err from MacOS output --- src/framework/control.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/control.ml b/src/framework/control.ml index 6159f1a936..3a3e86c596 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -215,6 +215,7 @@ struct let is_std = function | {vname = ("__tzname" | "__daylight" | "__timezone"); _} (* unix time.h *) | {vname = ("tzname" | "daylight" | "timezone"); _} (* unix time.h *) + | {vname = "getdate_err"; _} (* unix time.h (only on MacOS?) *) | {vname = ("stdin" | "stdout" | "stderr"); _} -> (* standard stdio.h *) true | _ -> false From 05429139cdeccac0b2cadb12e16c327e5df091f0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Oct 2022 16:34:22 +0300 Subject: [PATCH 014/566] Add test for broken mutex contents hiding on MacOS --- .../03-practical/28-base-mutex-macos.c | 20 +++++++++++++++++++ .../03-practical/28-base-mutex-macos.t | 13 ++++++++++++ tests/regression/03-practical/dune | 2 ++ 3 files changed, 35 insertions(+) create mode 100644 tests/regression/03-practical/28-base-mutex-macos.c create mode 100644 tests/regression/03-practical/28-base-mutex-macos.t create mode 100644 tests/regression/03-practical/dune diff --git a/tests/regression/03-practical/28-base-mutex-macos.c b/tests/regression/03-practical/28-base-mutex-macos.c new file mode 100644 index 0000000000..4f4dc417fc --- /dev/null +++ b/tests/regression/03-practical/28-base-mutex-macos.c @@ -0,0 +1,20 @@ +// Intentionally no #include , because we want to imitate/debug MacOS construction on anything. + +#define __PTHREAD_MUTEX_SIZE__ 56 + +struct _opaque_pthread_mutex_t { + long __sig; + char __opaque[__PTHREAD_MUTEX_SIZE__]; +}; + +typedef struct _opaque_pthread_mutex_t __darwin_pthread_mutex_t; +typedef __darwin_pthread_mutex_t pthread_mutex_t; + +#define _PTHREAD_MUTEX_SIG_init 0x32AAABA7 +#define PTHREAD_MUTEX_INITIALIZER {_PTHREAD_MUTEX_SIG_init, {0}} + +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +int main() { + return 0; +} diff --git a/tests/regression/03-practical/28-base-mutex-macos.t b/tests/regression/03-practical/28-base-mutex-macos.t new file mode 100644 index 0000000000..dd7f3f5ca0 --- /dev/null +++ b/tests/regression/03-practical/28-base-mutex-macos.t @@ -0,0 +1,13 @@ + $ goblint --enable witness.yaml.enabled --disable witness.invariant.accessed --set pre.cppflags[+] -DGOBLINT_NO_PTHREAD_ONCE 28-base-mutex-macos.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 2 + dead: 0 + total: 2 + [Info][Witness] witness generation summary: + total: 0 + +There should be no invariants about __sig. +Base analysis should hide mutex contents. + + $ yamlWitnessStrip < witness.yml + [] diff --git a/tests/regression/03-practical/dune b/tests/regression/03-practical/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/03-practical/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) From 7d9de044479fcecfefa20a91cedf37f0aa9b3b1e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Oct 2022 16:47:18 +0300 Subject: [PATCH 015/566] Fix 03-practical/28-base-mutex-macos --- src/analyses/base.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 2fec3d7500..e2855363af 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2151,7 +2151,7 @@ struct | _ -> () ); match lval with (* this section ensure global variables contain bottom values of the proper type before setting them *) - | (Var v, offs) when AD.is_definite lval_val && v.vglob -> + | (Var v, offs) when v.vglob -> (* Optimization: In case of simple integral types, we not need to evaluate the old value. v is not an allocated block, as v directly appears as a variable in the program; so no explicit check is required here (unlike in set) *) @@ -2163,14 +2163,14 @@ struct in begin match current_val with | `Bot -> (* current value is VD `Bot *) - begin match Addr.to_var_offset (AD.choose lval_val) with - | Some (x,offs) -> + begin match AD.to_var_offset lval_val with + | [(x,offs)] -> let t = v.vtype in let iv = VD.bot_value t in (* correct bottom value for top level variable *) - if M.tracing then M.tracel "set" "init bot value: %a\n" VD.pretty iv; + if M.tracing then M.tracel "set" "init bot value (%a): %a\n" d_plaintype t VD.pretty iv; let nv = VD.update_offset (Analyses.ask_of_ctx ctx) iv offs rval_val (Some (Lval lval)) lval t in (* do desired update to value *) set_savetop ~ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local (AD.from_var v) lval_t nv ~lval_raw:lval ~rval_raw:rval (* set top-level variable to updated value *) - | None -> + | _ -> set_savetop ~ctx (Analyses.ask_of_ctx ctx) ctx.global ctx.local lval_val lval_t rval_val ~lval_raw:lval ~rval_raw:rval end | _ -> From f49fd11c9e54920ac5ff520fad5aeb3b72aa1ffa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 7 Oct 2022 10:43:37 +0300 Subject: [PATCH 016/566] Add cram tests for CFG --- tests/regression/00-sanity/19-if-0.t | 27 +++ tests/regression/00-sanity/20-if-0-realnode.c | 1 + tests/regression/00-sanity/20-if-0-realnode.t | 25 ++ tests/regression/00-sanity/21-empty-loops.c | 1 + tests/regression/00-sanity/21-empty-loops.t | 215 ++++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100644 tests/regression/00-sanity/19-if-0.t create mode 100644 tests/regression/00-sanity/20-if-0-realnode.t create mode 100644 tests/regression/00-sanity/21-empty-loops.t diff --git a/tests/regression/00-sanity/19-if-0.t b/tests/regression/00-sanity/19-if-0.t new file mode 100644 index 0000000000..8aa27f87f1 --- /dev/null +++ b/tests/regression/00-sanity/19-if-0.t @@ -0,0 +1,27 @@ + $ goblint --enable exp.cfgdot 19-if-0.c + [Success][Assert] Assertion "1" will succeed (19-if-0.c:15:9-15:27) + [Warning][Deadcode] Function 'stuff' is uncalled: 1 LLoC (19-if-0.c:3:1-5:1) + [Warning][Deadcode] Function 'main' has dead code: + on line 11 (19-if-0.c:11-11) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 4 + dead: 2 (1 in uncalled functions) + total: 6 + [Warning][Deadcode][CWE-570] condition '0' is always false (19-if-0.c:9:9-9:10) + + $ cat cfgs/19-if-0.c/main.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 83 -> ret1669 [label = "return 0"] ; + 82 -> 83 [label = "__goblint_check(1)"] ; + 80 -> 83 [label = "stuff()"] ; + 78 -> 82 [label = "Neg(0)"] ; + fun1669 -> 78 [label = "(body)"] ; + 78 -> 80 [label = "Pos(0)"] ; + fun1669 [label="main()",shape=box]; + ret1669 [label="return of main()",shape=box]; + 78 [shape=diamond]; + 80 [fillcolor=orange]; + 82 []; + 83 []; + } diff --git a/tests/regression/00-sanity/20-if-0-realnode.c b/tests/regression/00-sanity/20-if-0-realnode.c index ebde535270..0e7e6f662f 100644 --- a/tests/regression/00-sanity/20-if-0-realnode.c +++ b/tests/regression/00-sanity/20-if-0-realnode.c @@ -1,3 +1,4 @@ +// CRAM #include void stuff() { diff --git a/tests/regression/00-sanity/20-if-0-realnode.t b/tests/regression/00-sanity/20-if-0-realnode.t new file mode 100644 index 0000000000..af48e4402c --- /dev/null +++ b/tests/regression/00-sanity/20-if-0-realnode.t @@ -0,0 +1,25 @@ + $ goblint --enable exp.cfgdot 20-if-0-realnode.c + [Warning][Deadcode] Function 'stuff' is uncalled: 1 LLoC (20-if-0-realnode.c:4:1-6:1) + [Warning][Deadcode] Function 'main' has dead code: + on line 13 (20-if-0-realnode.c:13-13) + on line 18 (20-if-0-realnode.c:18-18) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 2 + dead: 3 (1 in uncalled functions) + total: 5 + [Warning][Deadcode][CWE-570] condition '0' is always false (20-if-0-realnode.c:11:9-11:10) + + $ cat cfgs/20-if-0-realnode.c/main.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 82 -> ret1669 [label = "return 0"] ; + 80 -> 82 [label = "stuff()"] ; + 78 -> 80 [label = "Pos(0)"] ; + 78 -> 78 [label = "Neg(0)"] ; + fun1669 -> 78 [label = "(body)"] ; + fun1669 [label="main()",shape=box]; + ret1669 [label="return of main()",shape=box,fillcolor=orange]; + 78 [shape=diamond]; + 80 [fillcolor=orange]; + 82 [fillcolor=orange]; + } diff --git a/tests/regression/00-sanity/21-empty-loops.c b/tests/regression/00-sanity/21-empty-loops.c index f3f0feb2e5..220e5bf6e9 100644 --- a/tests/regression/00-sanity/21-empty-loops.c +++ b/tests/regression/00-sanity/21-empty-loops.c @@ -1,3 +1,4 @@ +// CRAM int main() { // non-deterministically make all variants live diff --git a/tests/regression/00-sanity/21-empty-loops.t b/tests/regression/00-sanity/21-empty-loops.t new file mode 100644 index 0000000000..8dad2ae3f1 --- /dev/null +++ b/tests/regression/00-sanity/21-empty-loops.t @@ -0,0 +1,215 @@ + $ goblint --enable exp.cfgdot 21-empty-loops.c + [Warning][Deadcode] Function 'suffix' is uncalled: 1 LLoC (21-empty-loops.c:66:1-69:1) + [Warning][Deadcode] Function 'f_empty_goto_loop_suffix' has dead code: + on line 76 (21-empty-loops.c:76-76) + [Warning][Deadcode] Function 'f_empty_while_loop' has dead code: + on line 64 (21-empty-loops.c:64-64) + [Warning][Deadcode] Function 'f_empty_while_loop_prefix' has dead code: + on line 124 (21-empty-loops.c:124-124) + [Warning][Deadcode] Function 'f_empty_while_loop_semicolon' has dead code: + on line 139 (21-empty-loops.c:139-139) + [Warning][Deadcode] Function 'f_empty_while_loop_suffix' has dead code: + on lines 83..84 (21-empty-loops.c:83-84) + [Warning][Deadcode] Function 'f_nonempty_while_loop' has dead code: + on line 104 (21-empty-loops.c:104-104) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 65 + dead: 8 (1 in uncalled functions) + total: 73 + [Warning][Deadcode][CWE-571] condition '1' is always true (21-empty-loops.c:63:10-63:11) + [Warning][Deadcode][CWE-571] condition '1' is always true (21-empty-loops.c:81:10-81:11) + [Warning][Deadcode][CWE-571] condition '1' is always true (21-empty-loops.c:100:10-100:11) + [Warning][Deadcode][CWE-571] condition '1' is always true (21-empty-loops.c:123:10-123:11) + [Warning][Deadcode][CWE-571] condition '1' is always true (21-empty-loops.c:136:10-136:11) + + $ cat cfgs/21-empty-loops.c/f_empty_goto_loop.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 10966185866 -> ret1667 [label = "return"] ; + 151 -> 10966185866 [label = "Neg(1)"] ; + 151 -> 151 [label = "skip"] ; + fun1667 -> 151 [label = "(body)"] ; + 10966185866 [fillcolor=orange]; + 151 []; + fun1667 [label="f_empty_goto_loop()",shape=box]; + ret1667 [label="return of f_empty_goto_loop()",shape=box,fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_empty_while_loop.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 158 -> ret1668 [label = "return"] ; + 155 -> 158 [label = "Neg(1)"] ; + 155 -> 155 [label = "Pos(1)"] ; + fun1668 -> 155 [label = "(body)"] ; + 155 [shape=diamond]; + 158 [fillcolor=orange]; + fun1668 [label="f_empty_while_loop()",shape=box]; + ret1668 [label="return of f_empty_while_loop()",shape=box,fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_empty_goto_loop_suffix.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 10494758249 -> ret1669 [label = "return"] ; + 160 -> 10494758249 [label = "Neg(1)"] ; + 162 -> 10494758249 [label = "suffix()"] ; + 160 -> 160 [label = "skip"] ; + fun1669 -> 160 [label = "(body)"] ; + 160 []; + 162 [fillcolor=orange]; + fun1669 [label="f_empty_goto_loop_suffix()",shape=box]; + ret1669 [label="return of f_empty_goto_loop_suffix()",shape=box,fillcolor=orange]; + 10494758249 [fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_empty_while_loop_suffix.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 171 -> ret1670 [label = "return"] ; + 170 -> 171 [label = "suffix()"] ; + 166 -> 170 [label = "Neg(1)"] ; + 166 -> 166 [label = "Pos(1)"] ; + fun1670 -> 166 [label = "(body)"] ; + fun1670 [label="f_empty_while_loop_suffix()",shape=box]; + 166 [shape=diamond]; + 170 [fillcolor=orange]; + 171 [fillcolor=orange]; + ret1670 [label="return of f_empty_while_loop_suffix()",shape=box,fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_nonempty_goto_loop.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 10142820772 -> ret1671 [label = "return"] ; + 174 -> 10142820772 [label = "Neg(1)"] ; + 174 -> 174 [label = "body()"] ; + fun1671 -> 174 [label = "(body)"] ; + 10142820772 [fillcolor=orange]; + fun1671 [label="f_nonempty_goto_loop()",shape=box]; + 174 []; + ret1671 [label="return of f_nonempty_goto_loop()",shape=box,fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_nonempty_while_loop.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 184 -> ret1672 [label = "return"] ; + 179 -> 184 [label = "Neg(1)"] ; + 182 -> 179 [label = "body()"] ; + fun1672 -> 179 [label = "(body)"] ; + 179 -> 182 [label = "Pos(1)"] ; + fun1672 [label="f_nonempty_while_loop()",shape=box]; + 179 [shape=diamond]; + 182 []; + 184 [fillcolor=orange]; + ret1672 [label="return of f_nonempty_while_loop()",shape=box,fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_empty_goto_loop_prefix.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 10451917286 -> ret1673 [label = "return"] ; + 188 -> 10451917286 [label = "Neg(1)"] ; + 188 -> 188 [label = "skip"] ; + 187 -> 188 [label = "prefix()"] ; + fun1673 -> 187 [label = "(body)"] ; + fun1673 [label="f_empty_goto_loop_prefix()",shape=box]; + 187 []; + 188 []; + ret1673 [label="return of f_empty_goto_loop_prefix()",shape=box,fillcolor=orange]; + 10451917286 [fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_empty_while_loop_prefix.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 197 -> ret1674 [label = "return"] ; + 194 -> 197 [label = "Neg(1)"] ; + 194 -> 194 [label = "Pos(1)"] ; + 190 -> 194 [label = "prefix()"] ; + fun1674 -> 190 [label = "(body)"] ; + fun1674 [label="f_empty_while_loop_prefix()",shape=box]; + 190 []; + 194 [shape=diamond]; + 197 [fillcolor=orange]; + ret1674 [label="return of f_empty_while_loop_prefix()",shape=box,fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_empty_goto_loop_semicolon.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 10404769065 -> ret1675 [label = "return"] ; + 198 -> 10404769065 [label = "Neg(1)"] ; + 198 -> 198 [label = "skip"] ; + fun1675 -> 198 [label = "(body)"] ; + 10404769065 [fillcolor=orange]; + fun1675 [label="f_empty_goto_loop_semicolon()",shape=box]; + 198 []; + ret1675 [label="return of f_empty_goto_loop_semicolon()",shape=box,fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_empty_while_loop_semicolon.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 206 -> ret1676 [label = "return"] ; + 203 -> 206 [label = "Neg(1)"] ; + 203 -> 203 [label = "Pos(1)"] ; + fun1676 -> 203 [label = "(body)"] ; + fun1676 [label="f_empty_while_loop_semicolon()",shape=box]; + ret1676 [label="return of f_empty_while_loop_semicolon()",shape=box,fillcolor=orange]; + 203 [shape=diamond]; + 206 [fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_empty_goto_loop_multiple.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 10218149751 -> ret1677 [label = "return"] ; + 207 -> 10218149751 [label = "Neg(1)"] ; + 207 -> 207 [label = "skip"] ; + fun1677 -> 207 [label = "(body)"] ; + fun1677 [label="f_empty_goto_loop_multiple()",shape=box]; + ret1677 [label="return of f_empty_goto_loop_multiple()",shape=box,fillcolor=orange]; + 207 []; + 10218149751 [fillcolor=orange]; + } + + $ cat cfgs/21-empty-loops.c/f_empty_goto_loop_multiple_semicolon_first.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 10606643600 -> ret1678 [label = "return"] ; + 209 -> 10606643600 [label = "Neg(1)"] ; + 209 -> 209 [label = "skip"] ; + fun1678 -> 209 [label = "(body)"] ; + 10606643600 [fillcolor=orange]; + fun1678 [label="f_empty_goto_loop_multiple_semicolon_first()",shape=box]; + ret1678 [label="return of f_empty_goto_loop_multiple_semicolon_first()",shape=box,fillcolor=orange]; + 209 []; + } + + $ cat cfgs/21-empty-loops.c/f_empty_goto_loop_multiple_semicolon_second.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 10984714377 -> ret1679 [label = "return"] ; + 212 -> 10984714377 [label = "Neg(1)"] ; + 212 -> 212 [label = "skip"] ; + fun1679 -> 212 [label = "(body)"] ; + 10984714377 [fillcolor=orange]; + fun1679 [label="f_empty_goto_loop_multiple_semicolon_second()",shape=box]; + ret1679 [label="return of f_empty_goto_loop_multiple_semicolon_second()",shape=box,fillcolor=orange]; + 212 []; + } + + $ cat cfgs/21-empty-loops.c/f_empty_goto_loop_multiple_semicolon_both.dot + digraph cfg { + node [id="\N",URL="javascript:show_info('\N');",style=filled,fillcolor=white]; + 10253493141 -> ret1680 [label = "return"] ; + 215 -> 10253493141 [label = "Neg(1)"] ; + 215 -> 215 [label = "skip"] ; + fun1680 -> 215 [label = "(body)"] ; + 10253493141 [fillcolor=orange]; + fun1680 [label="f_empty_goto_loop_multiple_semicolon_both()",shape=box]; + ret1680 [label="return of f_empty_goto_loop_multiple_semicolon_both()",shape=box,fillcolor=orange]; + 215 []; + } From b38e7a98689a2b1b84efbe829c52e30ae64f18da Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Jun 2023 12:15:23 +0300 Subject: [PATCH 017/566] Add ppx_deriving_lattice using ppx_easy_deriving --- dune-project | 1 + goblint.opam | 2 + goblint.opam.locked | 22 ++++-- goblint.opam.template | 1 + src/dune | 2 +- src/ppx/lattice/dune | 8 ++ src/ppx/lattice/ppx_deriving_lattice.ml | 97 +++++++++++++++++++++++++ 7 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 src/ppx/lattice/dune create mode 100644 src/ppx/lattice/ppx_deriving_lattice.ml diff --git a/dune-project b/dune-project index 2fbfb271fc..903148201e 100644 --- a/dune-project +++ b/dune-project @@ -32,6 +32,7 @@ ppx_deriving ppx_deriving_hash (ppx_deriving_yojson (>= 3.7.0)) + ppx_easy_deriving (ounit2 :with-test) (qcheck-ounit :with-test) (odoc :with-doc) diff --git a/goblint.opam b/goblint.opam index 678ad53d13..4eac826d63 100644 --- a/goblint.opam +++ b/goblint.opam @@ -29,6 +29,7 @@ depends: [ "ppx_deriving" "ppx_deriving_hash" "ppx_deriving_yojson" {>= "3.7.0"} + "ppx_easy_deriving" "ounit2" {with-test} "qcheck-ounit" {with-test} "odoc" {with-doc} @@ -76,6 +77,7 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] + [ "ppx_easy_deriving.~dev" "git+https://github.com/sim642/ppx_easy_deriving.git#3d599fdfb231e4a1f9bad0e914068210901533a4" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] # TODO: add back after release, only pinned for CI stability diff --git a/goblint.opam.locked b/goblint.opam.locked index acb49a7b14..7fb916100b 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -50,12 +50,14 @@ depends: [ "cpu" {= "2.0.0"} "csexp" {= "1.5.1"} "ctypes" {= "0.20.1"} - "dune" {= "3.6.1"} + "dune" {= "3.8.0"} "dune-build-info" {= "3.6.1"} "dune-configurator" {= "3.6.1"} "dune-private-libs" {= "3.6.1"} "dune-site" {= "3.6.1"} "dyn" {= "3.6.1"} + "either" {= "1.0.0"} + "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} "goblint-cil" {= "2.0.1"} @@ -67,11 +69,11 @@ depends: [ "mlgmpidl" {= "1.2.14"} "num" {= "1.4"} "ocaml" {= "4.14.0"} - "ocaml-variants" {= "4.14.0+options"} "ocaml-compiler-libs" {= "v0.12.4"} "ocaml-config" {= "2"} "ocaml-option-flambda" {= "1"} "ocaml-syntax-shims" {= "1.0.0"} + "ocaml-variants" {= "4.14.0+options"} "ocamlbuild" {= "0.14.2"} "ocamlfind" {= "1.9.5"} "odoc" {= "2.2.0" & with-doc} @@ -83,7 +85,8 @@ depends: [ "ppx_deriving" {= "5.2.1"} "ppx_deriving_hash" {= "0.1.1"} "ppx_deriving_yojson" {= "3.7.0"} - "ppxlib" {= "0.28.0"} + "ppx_easy_deriving" {= "~dev"} + "ppxlib" {= "0.30.0"} "qcheck-core" {= "0.20"} "qcheck-ounit" {= "0.20" & with-test} "re" {= "1.10.4" & with-doc} @@ -127,16 +130,21 @@ conflicts: [ ] # TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 pin-depends: [ - [ - "goblint-cil.2.0.1" - "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" - ] [ "apron.v0.9.13" "git+https://github.com/antoinemine/apron.git#1a8e91062c0d7d1e80333d19d5a432332bbbaec8" ] + [ + "goblint-cil.2.0.1" + "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" + ] [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] + [ + "ppx_easy_deriving.~dev" + "git+ssh://git@github.com/sim642/ppx_easy_deriving.git#master" +] ] + diff --git a/goblint.opam.template b/goblint.opam.template index b7f5a7abff..72855dd72e 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -3,6 +3,7 @@ available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ [ "goblint-cil.2.0.1" "git+https://github.com/goblint/cil.git#4df989fe625d91ce07d94afe1d85b3b5c6cdd63e" ] + [ "ppx_easy_deriving.~dev" "git+https://github.com/sim642/ppx_easy_deriving.git#3d599fdfb231e4a1f9bad0e914068210901533a4" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] # TODO: add back after release, only pinned for CI stability diff --git a/src/dune b/src/dune index 85944375ea..1ab30920e1 100644 --- a/src/dune +++ b/src/dune @@ -59,7 +59,7 @@ (foreign_stubs (language c) (names stubs)) (ocamlopt_flags :standard -no-float-const-prop) (preprocess - (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson ppx_blob)) + (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson ppx_blob ppx_deriving_lattice)) (preprocessor_deps (file util/options.schema.json)) (instrumentation (backend bisect_ppx)) ) diff --git a/src/ppx/lattice/dune b/src/ppx/lattice/dune new file mode 100644 index 0000000000..4c057c6fab --- /dev/null +++ b/src/ppx/lattice/dune @@ -0,0 +1,8 @@ +(include_subdirs no) + +(library + (name ppx_deriving_lattice) + (kind ppx_deriver) + (libraries ppxlib ppx_easy_deriving) + (ppx_runtime_libraries ppx_easy_deriving.runtime) + (preprocess (pps ppxlib.metaquot))) diff --git a/src/ppx/lattice/ppx_deriving_lattice.ml b/src/ppx/lattice/ppx_deriving_lattice.ml new file mode 100644 index 0000000000..e78e88644a --- /dev/null +++ b/src/ppx/lattice/ppx_deriving_lattice.ml @@ -0,0 +1,97 @@ +open Ppxlib +open Ppx_easy_deriving + +module LeqArg: Product.Reduce.Conjunctive.S = +struct + let name = "leq" +end + +module LeqDeriver = Deriver.Make (Product.Reduce2.Make (Product.Reduce.Conjunctive.Make (LeqArg))) +let leq_deriving = LeqDeriver.register () + + +module JoinArg: Product.Map2.S = +struct + let name = "join" +end + +module JoinDeriver = Deriver.Make (Product.Map2.Make (JoinArg)) +let join_deriving = JoinDeriver.register () + + +module MeetArg: Product.Map2.S = +struct + let name = "meet" +end + +module MeetDeriver = Deriver.Make (Product.Map2.Make (MeetArg)) +let meet_deriving = MeetDeriver.register () + + +module WidenArg: Product.Map2.S = +struct + let name = "widen" +end + +module WidenDeriver = Deriver.Make (Product.Map2.Make (WidenArg)) +let widen_deriving = WidenDeriver.register () + + +module NarrowArg: Product.Map2.S = +struct + let name = "narrow" +end + +module NarrowDeriver = Deriver.Make (Product.Map2.Make (NarrowArg)) +let narrow_deriving = NarrowDeriver.register () + + +module BotArg: Product.Create.S = +struct + let name = "bot" + let typ ~loc _ = [%type: unit] +end + +module BotDeriver = Deriver.Make (Product.Create.Make (BotArg)) +let bot_deriving = BotDeriver.register () + + +module IsBotArg: Product.Reduce.Conjunctive.S = +struct + let name = "is_bot" +end + +module IsBotDeriver = Deriver.Make (Product.Reduce1.Make (Product.Reduce.Conjunctive.Make (IsBotArg))) +let is_bot_deriving = IsBotDeriver.register () + + +module TopArg: Product.Create.S = +struct + let name = "top" + let typ ~loc _ = [%type: unit] +end + +module TopDeriver = Deriver.Make (Product.Create.Make (TopArg)) +let top_deriving = TopDeriver.register () + + +module IsTopArg: Product.Reduce.Conjunctive.S = +struct + let name = "is_top" +end + +module IsTopDeriver = Deriver.Make (Product.Reduce1.Make (Product.Reduce.Conjunctive.Make (IsTopArg))) +let is_top_deriving = IsTopDeriver.register () + + +let _ = Ppxlib.Deriving.add_alias "lattice" [ + leq_deriving; + join_deriving; + meet_deriving; + widen_deriving; + narrow_deriving; + bot_deriving; + is_bot_deriving; + top_deriving; + is_top_deriving; + ] From a4ef64ec4a44058c6f46131ac9e5f266124d6780 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 12 May 2021 11:44:48 +0300 Subject: [PATCH 018/566] Use lattice deriver for Lattice.ProdConf --- src/domains/lattice.ml | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/domains/lattice.ml b/src/domains/lattice.ml index 4cdaa8fb9f..0147939413 100644 --- a/src/domains/lattice.ml +++ b/src/domains/lattice.ml @@ -409,27 +409,17 @@ end module ProdConf (C: Printable.ProdConfiguration) (Base1: S) (Base2: S) = struct - include Printable.ProdConf (C) (Base1) (Base2) - - let bot () = (Base1.bot (), Base2.bot ()) - let is_bot (x1,x2) = Base1.is_bot x1 && Base2.is_bot x2 - let top () = (Base1.top (), Base2.top ()) - let is_top (x1,x2) = Base1.is_top x1 && Base2.is_top x2 - - let leq (x1,x2) (y1,y2) = Base1.leq x1 y1 && Base2.leq x2 y2 + open struct (* open to avoid leaking P and causing conflicts *) + module P = Printable.ProdConf (C) (Base1) (Base2) + end + type t = Base1.t * Base2.t [@@deriving lattice] + include (P: module type of P with type t := t) let pretty_diff () ((x1,x2:t),(y1,y2:t)): Pretty.doc = if Base1.leq x1 y1 then Base2.pretty_diff () (x2,y2) else Base1.pretty_diff () (x1,y1) - - let op_scheme op1 op2 (x1,x2) (y1,y2): t = (op1 x1 y1, op2 x2 y2) - let join = op_scheme Base1.join Base2.join - let meet = op_scheme Base1.meet Base2.meet - let narrow = op_scheme Base1.narrow Base2.narrow - let widen = op_scheme Base1.widen Base2.widen - end From 35fe676dd6af8424ecca1a0f25181ac71adf8400 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 12 May 2021 11:46:10 +0300 Subject: [PATCH 019/566] Use lattice deriver for Lattice.Prod3 --- src/domains/lattice.ml | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/domains/lattice.ml b/src/domains/lattice.ml index 0147939413..2ed13ad385 100644 --- a/src/domains/lattice.ml +++ b/src/domains/lattice.ml @@ -428,14 +428,11 @@ module ProdSimple = ProdConf (struct let expand_fst = false let expand_snd = fal module Prod3 (Base1: S) (Base2: S) (Base3: S) = struct - include Printable.Prod3 (Base1) (Base2) (Base3) - - let bot () = (Base1.bot (), Base2.bot (), Base3.bot ()) - let is_bot (x1,x2,x3) = Base1.is_bot x1 && Base2.is_bot x2 && Base3.is_bot x3 - let top () = (Base1.top (), Base2.top (), Base3.top ()) - let is_top (x1,x2,x3) = Base1.is_top x1 && Base2.is_top x2 && Base3.is_top x3 - - let leq (x1,x2,x3) (y1,y2,y3) = Base1.leq x1 y1 && Base2.leq x2 y2 && Base3.leq x3 y3 + open struct (* open to avoid leaking P and causing conflicts *) + module P = Printable.Prod3 (Base1) (Base2) (Base3) + end + type t = Base1.t * Base2.t * Base3.t [@@deriving lattice] + include (P: module type of P with type t := t) let pretty_diff () ((x1,x2,x3:t),(y1,y2,y3:t)): Pretty.doc = if not (Base1.leq x1 y1) then @@ -444,12 +441,6 @@ struct Base2.pretty_diff () (x2,y2) else Base3.pretty_diff () (x3,y3) - - let op_scheme op1 op2 op3 (x1,x2,x3) (y1,y2,y3): t = (op1 x1 y1, op2 x2 y2, op3 x3 y3) - let join = op_scheme Base1.join Base2.join Base3.join - let meet = op_scheme Base1.meet Base2.meet Base3.meet - let widen = op_scheme Base1.widen Base2.widen Base3.widen - let narrow = op_scheme Base1.narrow Base2.narrow Base3.narrow end module Prod4 (Base1: S) (Base2: S) (Base3: S) (Base4: S) = From 7989ea4d4a1bd8cf9bd721888cddba12b782bed0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Jun 2023 12:27:08 +0300 Subject: [PATCH 020/566] Remove unused Prod4 --- src/domains/lattice.ml | 27 --------------------------- src/domains/printable.ml | 29 ----------------------------- 2 files changed, 56 deletions(-) diff --git a/src/domains/lattice.ml b/src/domains/lattice.ml index 2ed13ad385..01cbf9b9cf 100644 --- a/src/domains/lattice.ml +++ b/src/domains/lattice.ml @@ -443,33 +443,6 @@ struct Base3.pretty_diff () (x3,y3) end -module Prod4 (Base1: S) (Base2: S) (Base3: S) (Base4: S) = -struct - include Printable.Prod4 (Base1) (Base2) (Base3) (Base4) - - let bot () = (Base1.bot (), Base2.bot (), Base3.bot (), Base4.bot ()) - let is_bot (x1,x2,x3,x4) = Base1.is_bot x1 && Base2.is_bot x2 && Base3.is_bot x3 && Base4.is_bot x4 - let top () = (Base1.top (), Base2.top (), Base3.top (), Base4.top ()) - let is_top (x1,x2,x3,x4) = Base1.is_top x1 && Base2.is_top x2 && Base3.is_top x3 && Base4.is_top x4 - let leq (x1,x2,x3,x4) (y1,y2,y3,y4) = Base1.leq x1 y1 && Base2.leq x2 y2 && Base3.leq x3 y3 && Base4.leq x4 y4 - - let pretty_diff () ((x1,x2,x3,x4:t),(y1,y2,y3,y4:t)): Pretty.doc = - if not (Base1.leq x1 y1) then - Base1.pretty_diff () (x1,y1) - else if not (Base2.leq x2 y2) then - Base2.pretty_diff () (x2,y2) - else if not (Base3.leq x3 y3) then - Base3.pretty_diff () (x3,y3) - else - Base4.pretty_diff () (x4,y4) - - let op_scheme op1 op2 op3 op4 (x1,x2,x3,x4) (y1,y2,y3,y4): t = (op1 x1 y1, op2 x2 y2, op3 x3 y3, op4 x4 y4) - let join = op_scheme Base1.join Base2.join Base3.join Base4.join - let meet = op_scheme Base1.meet Base2.meet Base3.meet Base4.meet - let widen = op_scheme Base1.widen Base2.widen Base3.widen Base4.widen - let narrow = op_scheme Base1.narrow Base2.narrow Base3.narrow Base4.narrow -end - module LiftBot (Base : S) = struct include Printable.LiftBot (Base) diff --git a/src/domains/printable.ml b/src/domains/printable.ml index 59d22957b4..495d294e6e 100644 --- a/src/domains/printable.ml +++ b/src/domains/printable.ml @@ -430,35 +430,6 @@ struct let arbitrary () = QCheck.triple (Base1.arbitrary ()) (Base2.arbitrary ()) (Base3.arbitrary ()) end -module Prod4 (Base1: S) (Base2: S) (Base3: S) (Base4: S) = struct - type t = Base1.t * Base2.t * Base3.t * Base4.t [@@deriving eq, ord, hash] - include Std - - let show (x,y,z,w) = "(" ^ Base1.show x ^ ", " ^ Base2.show y ^ ", " ^ Base3.show z ^ ", " ^ Base4.show w ^ ")" - - let pretty () (x,y,z,w) = - text "(" ++ - Base1.pretty () x - ++ text ", " ++ - Base2.pretty () y - ++ text ", " ++ - Base3.pretty () z - ++ text ", " ++ - Base4.pretty () w - ++ text ")" - - let printXml f (x,y,z,w) = - BatPrintf.fprintf f "\n\n\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n\n" (XmlUtil.escape (Base1.name ())) Base1.printXml x (XmlUtil.escape (Base2.name ())) Base2.printXml y (XmlUtil.escape (Base3.name ())) Base3.printXml z (XmlUtil.escape (Base4.name ())) Base4.printXml w - - let to_yojson (x, y, z, w) = - `Assoc [ (Base1.name (), Base1.to_yojson x); (Base2.name (), Base2.to_yojson y); (Base3.name (), Base3.to_yojson z); (Base4.name (), Base4.to_yojson w) ] - - let name () = Base1.name () ^ " * " ^ Base2.name () ^ " * " ^ Base3.name () ^ " * " ^ Base4.name () - - let relift (x,y,z,w) = (Base1.relift x, Base2.relift y, Base3.relift z, Base4.relift w) - let arbitrary () = QCheck.quad (Base1.arbitrary ()) (Base2.arbitrary ()) (Base3.arbitrary ()) (Base4.arbitrary ()) -end - module Liszt (Base: S) = struct type t = Base.t list [@@deriving eq, ord, hash, to_yojson] From a7d42a44cc0cc3843a74a3b789889641b95a7e2b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Jun 2023 12:35:55 +0300 Subject: [PATCH 021/566] Use lattice deriver for BaseDomain.BaseComponents --- src/cdomains/baseDomain.ml | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/cdomains/baseDomain.ml b/src/cdomains/baseDomain.ml index 6950c16889..e3a775020f 100644 --- a/src/cdomains/baseDomain.ml +++ b/src/cdomains/baseDomain.ml @@ -45,7 +45,7 @@ type 'a basecomponents_t = { deps: PartDeps.t; weak: WeakUpdates.t; priv: 'a; -} [@@deriving eq, ord, hash] +} [@@deriving eq, ord, hash, lattice] module BaseComponents (PrivD: Lattice.S): @@ -54,7 +54,7 @@ sig val op_scheme: (CPA.t -> CPA.t -> CPA.t) -> (PartDeps.t -> PartDeps.t -> PartDeps.t) -> (WeakUpdates.t -> WeakUpdates.t -> WeakUpdates.t) -> (PrivD.t -> PrivD.t -> PrivD.t) -> t -> t -> t end = struct - type t = PrivD.t basecomponents_t [@@deriving eq, ord, hash] + type t = PrivD.t basecomponents_t [@@deriving eq, ord, hash, lattice] include Printable.Std open Pretty @@ -97,14 +97,6 @@ struct let tr = QCheck.quad (CPA.arbitrary ()) (PartDeps.arbitrary ()) (WeakUpdates.arbitrary ()) (PrivD.arbitrary ()) in QCheck.map ~rev:to_tuple of_tuple tr - let bot () = { cpa = CPA.bot (); deps = PartDeps.bot (); weak = WeakUpdates.bot (); priv = PrivD.bot ()} - let is_bot {cpa; deps; weak; priv} = CPA.is_bot cpa && PartDeps.is_bot deps && WeakUpdates.is_bot weak && PrivD.is_bot priv - let top () = {cpa = CPA.top (); deps = PartDeps.top (); weak = WeakUpdates.top () ; priv = PrivD.bot ()} - let is_top {cpa; deps; weak; priv} = CPA.is_top cpa && PartDeps.is_top deps && WeakUpdates.is_top weak && PrivD.is_top priv - - let leq {cpa=x1; deps=x2; weak=x3; priv=x4 } {cpa=y1; deps=y2; weak=y3; priv=y4} = - CPA.leq x1 y1 && PartDeps.leq x2 y2 && WeakUpdates.leq x3 y3 && PrivD.leq x4 y4 - let pretty_diff () (({cpa=x1; deps=x2; weak=x3; priv=x4}:t),({cpa=y1; deps=y2; weak=y3; priv=y4}:t)): Pretty.doc = if not (CPA.leq x1 y1) then CPA.pretty_diff () (x1,y1) @@ -117,10 +109,6 @@ struct let op_scheme op1 op2 op3 op4 {cpa=x1; deps=x2; weak=x3; priv=x4} {cpa=y1; deps=y2; weak=y3; priv=y4}: t = {cpa = op1 x1 y1; deps = op2 x2 y2; weak = op3 x3 y3; priv = op4 x4 y4 } - let join = op_scheme CPA.join PartDeps.join WeakUpdates.join PrivD.join - let meet = op_scheme CPA.meet PartDeps.meet WeakUpdates.meet PrivD.meet - let widen = op_scheme CPA.widen PartDeps.widen WeakUpdates.widen PrivD.widen - let narrow = op_scheme CPA.narrow PartDeps.narrow WeakUpdates.narrow PrivD.narrow let relift {cpa; deps; weak; priv} = {cpa = CPA.relift cpa; deps = PartDeps.relift deps; weak = WeakUpdates.relift weak; priv = PrivD.relift priv} From 9a34f8c3a6e353053cdac5edfc7b35a978ea43f0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Jun 2023 12:37:45 +0300 Subject: [PATCH 022/566] Use lattice deriver for RelationDomain.RelComponents --- src/cdomains/apron/relationDomain.apron.ml | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/cdomains/apron/relationDomain.apron.ml b/src/cdomains/apron/relationDomain.apron.ml index c5b6a0a89b..c8942c2c6b 100644 --- a/src/cdomains/apron/relationDomain.apron.ml +++ b/src/cdomains/apron/relationDomain.apron.ml @@ -151,7 +151,7 @@ end type ('a, 'b) relcomponents_t = { rel: 'a; priv: 'b; -} [@@deriving eq, ord, hash, to_yojson] +} [@@deriving eq, ord, hash, to_yojson, lattice] module RelComponents (D3: S3) (PrivD: Lattice.S): sig @@ -160,7 +160,7 @@ sig end = struct module RD = D3 - type t = (RD.t, PrivD.t) relcomponents_t [@@deriving eq, ord, hash, to_yojson] + type t = (RD.t, PrivD.t) relcomponents_t [@@deriving eq, ord, hash, to_yojson, lattice] include Printable.Std open Pretty @@ -191,26 +191,11 @@ struct let tr = QCheck.pair (RD.arbitrary ()) (PrivD.arbitrary ()) in QCheck.map ~rev:to_tuple of_tuple tr - let bot () = {rel = RD.bot (); priv = PrivD.bot ()} - let is_bot {rel; priv} = RD.is_bot rel && PrivD.is_bot priv - let top () = {rel = RD.top (); priv = PrivD.bot ()} - let is_top {rel; priv} = RD.is_top rel && PrivD.is_top priv - - let leq {rel=x1; priv=x3 } {rel=y1; priv=y3} = - RD.leq x1 y1 && PrivD.leq x3 y3 - let pretty_diff () (({rel=x1; priv=x3}:t),({rel=y1; priv=y3}:t)): Pretty.doc = if not (RD.leq x1 y1) then RD.pretty_diff () (x1,y1) else PrivD.pretty_diff () (x3,y3) - - let op_scheme op1 op3 {rel=x1; priv=x3} {rel=y1; priv=y3}: t = - {rel = op1 x1 y1; priv = op3 x3 y3 } - let join = op_scheme RD.join PrivD.join - let meet = op_scheme RD.meet PrivD.meet - let widen = op_scheme RD.widen PrivD.widen - let narrow = op_scheme RD.narrow PrivD.narrow end From b0c207f141bc5336754ee1a479a7d7a2520cd7b3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Jun 2023 12:39:42 +0300 Subject: [PATCH 023/566] Use lattice deriver for PthreadDomain.D --- src/cdomains/pthreadDomain.ml | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/cdomains/pthreadDomain.ml b/src/cdomains/pthreadDomain.ml index 8cef57bdbd..145da447f6 100644 --- a/src/cdomains/pthreadDomain.ml +++ b/src/cdomains/pthreadDomain.ml @@ -25,8 +25,7 @@ end module D = struct include Printable.StdLeaf - type domain = { tid : Tid.t; pred : Pred.t; ctx : Ctx.t } [@@deriving to_yojson] - type t = domain + type t = { tid : Tid.t; pred : Pred.t; ctx : Ctx.t } [@@deriving to_yojson, lattice] (** printing *) let show x = @@ -36,7 +35,7 @@ module D = struct (Pred.show x.pred) (Ctx.show x.ctx) - include Printable.SimpleShow(struct type t = domain let show = show end) + include Printable.SimpleShow(struct type nonrec t = t let show = show end) let name () = "pthread state" @@ -59,22 +58,11 @@ module D = struct (** let hash = Hashtbl.hash *) let hash x = Hashtbl.hash (Tid.hash x.tid, Pred.hash x.pred, Ctx.hash x.ctx) let make tid pred ctx = { tid; pred; ctx } - let bot () = { tid = Tid.bot (); pred = Pred.bot (); ctx = Ctx.bot () } - let is_bot x = Tid.is_bot x.tid && Pred.is_bot x.pred && Ctx.is_bot x.ctx let any_is_bot x = Tid.is_bot x.tid || Pred.is_bot x.pred - let top () = { tid = Tid.top (); pred = Pred.top (); ctx = Ctx.top () } - let is_top x = Tid.is_top x.tid && Pred.is_top x.pred && Ctx.is_top x.ctx - - let leq x y = Tid.leq x.tid y.tid && Pred.leq x.pred y.pred && Ctx.leq x.ctx y.ctx let op_scheme op1 op2 op3 x y : t = { tid = op1 x.tid y.tid; pred = op2 x.pred y.pred; ctx = op3 x.ctx y.ctx } - let join = op_scheme Tid.join Pred.join Ctx.join - let widen = join - let meet = op_scheme Tid.meet Pred.meet Ctx.meet - let narrow = meet - let pretty_diff () (x,y) = if not (Tid.leq x.tid y.tid) then Tid.pretty_diff () (x.tid,y.tid) From a96262ef23ac2c8a7611327eb27ee65377fcfd5a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Jun 2023 12:41:00 +0300 Subject: [PATCH 024/566] Use more derivers for PthreadDomain.D --- src/cdomains/pthreadDomain.ml | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/cdomains/pthreadDomain.ml b/src/cdomains/pthreadDomain.ml index 145da447f6..7bed37a267 100644 --- a/src/cdomains/pthreadDomain.ml +++ b/src/cdomains/pthreadDomain.ml @@ -25,7 +25,7 @@ end module D = struct include Printable.StdLeaf - type t = { tid : Tid.t; pred : Pred.t; ctx : Ctx.t } [@@deriving to_yojson, lattice] + type t = { tid : Tid.t; pred : Pred.t; ctx : Ctx.t } [@@deriving eq, ord, hash, to_yojson, lattice] (** printing *) let show x = @@ -35,34 +35,16 @@ module D = struct (Pred.show x.pred) (Ctx.show x.ctx) - include Printable.SimpleShow(struct type nonrec t = t let show = show end) + include Printable.SimpleShow (struct + type nonrec t = t + let show = show + end) let name () = "pthread state" - (** let equal = Util.equals *) - let equal x y = - Tid.equal x.tid y.tid && Pred.equal x.pred y.pred && Ctx.equal x.ctx y.ctx - - - (** compare all fields with correspoding compare operators *) - let compare x y = - List.fold_left - (fun acc v -> if acc = 0 && v <> 0 then v else acc) - 0 - [ Tid.compare x.tid y.tid - ; Pred.compare x.pred y.pred - ; Ctx.compare x.ctx y.ctx - ] - - - (** let hash = Hashtbl.hash *) - let hash x = Hashtbl.hash (Tid.hash x.tid, Pred.hash x.pred, Ctx.hash x.ctx) let make tid pred ctx = { tid; pred; ctx } let any_is_bot x = Tid.is_bot x.tid || Pred.is_bot x.pred - let op_scheme op1 op2 op3 x y : t = - { tid = op1 x.tid y.tid; pred = op2 x.pred y.pred; ctx = op3 x.ctx y.ctx } - let pretty_diff () (x,y) = if not (Tid.leq x.tid y.tid) then Tid.pretty_diff () (x.tid,y.tid) From 17aa06c5e80e75d869d0b74797534c16060f6d61 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Jun 2023 12:46:24 +0300 Subject: [PATCH 025/566] Derive hash for IntDomain and FloatDomain --- src/cdomains/floatDomain.ml | 6 +----- src/cdomains/intDomain.ml | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/cdomains/floatDomain.ml b/src/cdomains/floatDomain.ml index 4eb024adf9..23e967ca65 100644 --- a/src/cdomains/floatDomain.ml +++ b/src/cdomains/floatDomain.ml @@ -939,7 +939,7 @@ module FloatDomTupleImpl = struct module F1 = FloatIntervalImplLifted open Batteries - type t = F1.t option [@@deriving to_yojson, eq, ord] + type t = F1.t option [@@deriving eq, ord, hash] let name () = "floatdomtuple" @@ -986,10 +986,6 @@ module FloatDomTupleImpl = struct Option.map_default identity "" (mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) x -> F.name () ^ ":" ^ F.show x); } x) - let hash x = - Option.map_default identity 0 - (mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.hash); } x) - let of_const fkind = create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.of_const fkind); } diff --git a/src/cdomains/intDomain.ml b/src/cdomains/intDomain.ml index 589239810f..d70d5b8ac1 100644 --- a/src/cdomains/intDomain.ml +++ b/src/cdomains/intDomain.ml @@ -3313,7 +3313,7 @@ module IntDomTupleImpl = struct module I5 = IntervalSetFunctor (BI) type t = I1.t option * I2.t option * I3.t option * I4.t option * I5.t option - [@@deriving to_yojson, eq, ord] + [@@deriving eq, ord, hash] let name () = "intdomtuple" @@ -3623,7 +3623,6 @@ module IntDomTupleImpl = struct (* others *) let show = String.concat "; " % to_list % mapp { fp = fun (type a) (module I:SOverflow with type t = a) x -> I.name () ^ ":" ^ (I.show x) } let to_yojson = [%to_yojson: Yojson.Safe.t list] % to_list % mapp { fp = fun (type a) (module I:SOverflow with type t = a) x -> I.to_yojson x } - let hash = List.fold_left (lxor) 0 % to_list % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.hash } (* `map/opt_map` are used by `project` *) let opt_map b f = From 5ed8049a1f308ff8d14675d0baeccc2eb30390df Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Jun 2023 13:20:47 +0300 Subject: [PATCH 026/566] Add ppx_deriving_printable using ppx_easy_deriving --- src/dune | 2 +- src/ppx/printable/dune | 8 ++++++++ src/ppx/printable/ppx_deriving_printable.ml | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/ppx/printable/dune create mode 100644 src/ppx/printable/ppx_deriving_printable.ml diff --git a/src/dune b/src/dune index 1ab30920e1..e7b4733abd 100644 --- a/src/dune +++ b/src/dune @@ -59,7 +59,7 @@ (foreign_stubs (language c) (names stubs)) (ocamlopt_flags :standard -no-float-const-prop) (preprocess - (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson ppx_blob ppx_deriving_lattice)) + (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson ppx_blob ppx_deriving_printable ppx_deriving_lattice)) (preprocessor_deps (file util/options.schema.json)) (instrumentation (backend bisect_ppx)) ) diff --git a/src/ppx/printable/dune b/src/ppx/printable/dune new file mode 100644 index 0000000000..2b620d2319 --- /dev/null +++ b/src/ppx/printable/dune @@ -0,0 +1,8 @@ +(include_subdirs no) + +(library + (name ppx_deriving_printable) + (kind ppx_deriver) + (libraries ppxlib ppx_easy_deriving) + (ppx_runtime_libraries ppx_easy_deriving.runtime) + (preprocess (pps ppxlib.metaquot))) diff --git a/src/ppx/printable/ppx_deriving_printable.ml b/src/ppx/printable/ppx_deriving_printable.ml new file mode 100644 index 0000000000..ad57b93e45 --- /dev/null +++ b/src/ppx/printable/ppx_deriving_printable.ml @@ -0,0 +1,15 @@ +open Ppx_easy_deriving + +module ReliftArg: Product.Map1.S = +struct + let name = "relift" +end + +module ReliftDeriver = Deriver.Make (Product.Map1.Make (ReliftArg)) +let relift_deriving = ReliftDeriver.register () + + +(* TODO: needs https://github.com/ocaml-ppx/ppxlib/pull/124 to include eq, ord, hash *) +(* let _ = Ppxlib.Deriving.add_alias "printable" [ + relift_deriving; + ] *) From 816a6f56f6c5302997d7bc0984c027b7cd126ece Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Jun 2023 13:21:24 +0300 Subject: [PATCH 027/566] Use relift deriver with lattice deriver --- src/cdomains/apron/relationDomain.apron.ml | 6 ++---- src/cdomains/baseDomain.ml | 7 ++----- src/cdomains/pthreadDomain.ml | 2 +- src/domains/printable.ml | 7 ++----- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/cdomains/apron/relationDomain.apron.ml b/src/cdomains/apron/relationDomain.apron.ml index c8942c2c6b..fdaced26ca 100644 --- a/src/cdomains/apron/relationDomain.apron.ml +++ b/src/cdomains/apron/relationDomain.apron.ml @@ -151,7 +151,7 @@ end type ('a, 'b) relcomponents_t = { rel: 'a; priv: 'b; -} [@@deriving eq, ord, hash, to_yojson, lattice] +} [@@deriving eq, ord, hash, to_yojson, relift, lattice] module RelComponents (D3: S3) (PrivD: Lattice.S): sig @@ -160,13 +160,11 @@ sig end = struct module RD = D3 - type t = (RD.t, PrivD.t) relcomponents_t [@@deriving eq, ord, hash, to_yojson, lattice] + type t = (RD.t, PrivD.t) relcomponents_t [@@deriving eq, ord, hash, to_yojson, relift, lattice] include Printable.Std open Pretty - let relift {rel; priv} = {rel = RD.relift rel; priv = PrivD.relift priv} - let show r = let first = RD.show r.rel in let third = PrivD.show r.priv in diff --git a/src/cdomains/baseDomain.ml b/src/cdomains/baseDomain.ml index e3a775020f..1c1a3c6a66 100644 --- a/src/cdomains/baseDomain.ml +++ b/src/cdomains/baseDomain.ml @@ -45,7 +45,7 @@ type 'a basecomponents_t = { deps: PartDeps.t; weak: WeakUpdates.t; priv: 'a; -} [@@deriving eq, ord, hash, lattice] +} [@@deriving eq, ord, hash, relift, lattice] module BaseComponents (PrivD: Lattice.S): @@ -54,7 +54,7 @@ sig val op_scheme: (CPA.t -> CPA.t -> CPA.t) -> (PartDeps.t -> PartDeps.t -> PartDeps.t) -> (WeakUpdates.t -> WeakUpdates.t -> WeakUpdates.t) -> (PrivD.t -> PrivD.t -> PrivD.t) -> t -> t -> t end = struct - type t = PrivD.t basecomponents_t [@@deriving eq, ord, hash, lattice] + type t = PrivD.t basecomponents_t [@@deriving eq, ord, hash, relift, lattice] include Printable.Std open Pretty @@ -109,9 +109,6 @@ struct let op_scheme op1 op2 op3 op4 {cpa=x1; deps=x2; weak=x3; priv=x4} {cpa=y1; deps=y2; weak=y3; priv=y4}: t = {cpa = op1 x1 y1; deps = op2 x2 y2; weak = op3 x3 y3; priv = op4 x4 y4 } - - let relift {cpa; deps; weak; priv} = - {cpa = CPA.relift cpa; deps = PartDeps.relift deps; weak = WeakUpdates.relift weak; priv = PrivD.relift priv} end module type ExpEvaluator = diff --git a/src/cdomains/pthreadDomain.ml b/src/cdomains/pthreadDomain.ml index 7bed37a267..a26cd3dfa5 100644 --- a/src/cdomains/pthreadDomain.ml +++ b/src/cdomains/pthreadDomain.ml @@ -25,7 +25,7 @@ end module D = struct include Printable.StdLeaf - type t = { tid : Tid.t; pred : Pred.t; ctx : Ctx.t } [@@deriving eq, ord, hash, to_yojson, lattice] + type t = { tid : Tid.t; pred : Pred.t; ctx : Ctx.t } [@@deriving eq, ord, hash, relift, to_yojson, lattice] (** printing *) let show x = diff --git a/src/domains/printable.ml b/src/domains/printable.ml index 495d294e6e..625af0e0c7 100644 --- a/src/domains/printable.ml +++ b/src/domains/printable.ml @@ -356,7 +356,7 @@ module ProdConf (C: ProdConfiguration) (Base1: S) (Base2: S)= struct include C - type t = Base1.t * Base2.t [@@deriving eq, ord, hash] + type t = Base1.t * Base2.t [@@deriving eq, ord, hash, relift] include Std @@ -387,8 +387,6 @@ struct `Assoc [ (Base1.name (), Base1.to_yojson x); (Base2.name (), Base2.to_yojson y) ] let arbitrary () = QCheck.pair (Base1.arbitrary ()) (Base2.arbitrary ()) - - let relift (x,y) = (Base1.relift x, Base2.relift y) end module Prod = ProdConf (struct let expand_fst = true let expand_snd = true end) @@ -396,7 +394,7 @@ module ProdSimple = ProdConf (struct let expand_fst = false let expand_snd = fal module Prod3 (Base1: S) (Base2: S) (Base3: S) = struct - type t = Base1.t * Base2.t * Base3.t [@@deriving eq, ord, hash] + type t = Base1.t * Base2.t * Base3.t [@@deriving eq, ord, hash, relift] include Std let show (x,y,z) = @@ -426,7 +424,6 @@ struct let name () = Base1.name () ^ " * " ^ Base2.name () ^ " * " ^ Base3.name () - let relift (x,y,z) = (Base1.relift x, Base2.relift y, Base3.relift z) let arbitrary () = QCheck.triple (Base1.arbitrary ()) (Base2.arbitrary ()) (Base3.arbitrary ()) end From 2e2bcc3e2ec63695d67a559b578e71886385e66c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Jun 2023 13:24:13 +0300 Subject: [PATCH 028/566] Use relift deriver more --- src/cdomains/mHP.ml | 5 +---- src/framework/analyses.ml | 3 +-- src/ppx/printable/ppx_deriving_printable.ml | 1 + 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/cdomains/mHP.ml b/src/cdomains/mHP.ml index 8037cfa21d..f0f2fc8c12 100644 --- a/src/cdomains/mHP.ml +++ b/src/cdomains/mHP.ml @@ -11,10 +11,7 @@ type t = { tid: ThreadIdDomain.ThreadLifted.t; created: ConcDomain.ThreadSet.t; must_joined: ConcDomain.ThreadSet.t; -} [@@deriving eq, ord, hash] - -let relift {tid; created; must_joined} = - {tid = ThreadIdDomain.ThreadLifted.relift tid; created = ConcDomain.ThreadSet.relift created; must_joined = ConcDomain.ThreadSet.relift must_joined} +} [@@deriving eq, ord, hash, relift] let current (ask:Queries.ask) = { diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 1a3a4ebeb1..a40a3b0bb5 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -32,8 +32,7 @@ end module Var = struct - type t = Node.t [@@deriving eq, ord, hash] - let relift = Node.relift + type t = Node.t [@@deriving eq, ord, hash, relift] let printXml f n = let l = Node.location n in diff --git a/src/ppx/printable/ppx_deriving_printable.ml b/src/ppx/printable/ppx_deriving_printable.ml index ad57b93e45..b8b80d6730 100644 --- a/src/ppx/printable/ppx_deriving_printable.ml +++ b/src/ppx/printable/ppx_deriving_printable.ml @@ -5,6 +5,7 @@ struct let name = "relift" end +(* TODO: Map1 should also do variants *) module ReliftDeriver = Deriver.Make (Product.Map1.Make (ReliftArg)) let relift_deriving = ReliftDeriver.register () From 57c5c3f2ff6838150c4e99e703026a5ae9c599b7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 25 Sep 2023 11:37:24 +0300 Subject: [PATCH 029/566] Add conditional accesses to LibraryDsl --- src/analyses/libraryDsl.ml | 50 ++++++++++++++++++++++++++----------- src/analyses/libraryDsl.mli | 29 ++++++++++++--------- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/analyses/libraryDsl.ml b/src/analyses/libraryDsl.ml index 49aac8ce9b..431d778b13 100644 --- a/src/analyses/libraryDsl.ml +++ b/src/analyses/libraryDsl.ml @@ -30,8 +30,20 @@ struct | [] -> fail "^::" end +type access = + | Access of LibraryDesc.Access.t + | If of (unit -> bool) * access + +let rec eval_access = function + | Access acc -> Some acc + | If (p, access) -> + if p () then + eval_access access + else + None + type ('k, 'l, 'r) arg_desc = { - accesses: Access.t list; + accesses: access list; match_arg: (Cil.exp, 'k, 'r) Pattern.t; match_var_args: (Cil.exp list, 'l, 'r) Pattern.t; } @@ -51,15 +63,21 @@ let rec accs: type k r. (k, r) args_desc -> Accesses.t = fun args_desc args -> match args_desc, args with | [], [] -> [] | VarArgs arg_desc, args -> - List.map (fun acc -> - (acc, args) + List.filter_map (fun access -> + match eval_access access with + | Some acc -> Some (acc, args) + | None -> None ) arg_desc.accesses | arg_desc :: args_desc, arg :: args -> let accs'' = accs args_desc args in - List.fold_left (fun (accs'': (Access.t * Cil.exp list) list) (acc: Access.t) -> - match List.assoc_opt acc accs'' with - | Some args -> (acc, arg :: args) :: List.remove_assoc acc accs'' - | None -> (acc, [arg]) :: accs'' + List.fold_left (fun (accs'': (Access.t * Cil.exp list) list) (access: access) -> + match eval_access access with + | Some acc -> + begin match List.assoc_opt acc accs'' with + | Some args -> (acc, arg :: args) :: List.remove_assoc acc accs'' + | None -> (acc, [arg]) :: accs'' + end + | None -> accs'' ) accs'' arg_desc.accesses | _, _ -> invalid_arg "accs" @@ -94,11 +112,13 @@ let drop (_name: string) accesses = { empty_drop_desc with accesses; } let drop' accesses = { empty_drop_desc with accesses; } -let r = Access.{ kind = Read; deep = false; } -let r_deep = Access.{ kind = Read; deep = true; } -let w = Access.{ kind = Write; deep = false; } -let w_deep = Access.{ kind = Write; deep = true; } -let f = Access.{ kind = Free; deep = false; } -let f_deep = Access.{ kind = Free; deep = true; } -let s = Access.{ kind = Spawn; deep = false; } -let s_deep = Access.{ kind = Spawn; deep = true; } +let r = Access { kind = Read; deep = false; } +let r_deep = Access { kind = Read; deep = true; } +let w = Access { kind = Write; deep = false; } +let w_deep = Access { kind = Write; deep = true; } +let f = Access { kind = Free; deep = false; } +let f_deep = Access { kind = Free; deep = true; } +let s = Access { kind = Spawn; deep = false; } +let s_deep = Access { kind = Spawn; deep = true; } + +let if_ p access = If (p, access) diff --git a/src/analyses/libraryDsl.mli b/src/analyses/libraryDsl.mli index fd0bc45c26..4ea6d7dae4 100644 --- a/src/analyses/libraryDsl.mli +++ b/src/analyses/libraryDsl.mli @@ -28,46 +28,51 @@ val special': ?attrs:LibraryDesc.attr list -> (LibraryDesc.special, LibraryDesc. (** Create unknown library function descriptor from arguments descriptor, which must {!drop} all arguments. *) val unknown: ?attrs:LibraryDesc.attr list -> (LibraryDesc.special, LibraryDesc.special) args_desc -> LibraryDesc.t +(** Argument access descriptor. *) +type access (** Argument descriptor, which captures the named argument with accesses for continuation function of {!special}. *) -val __: string -> LibraryDesc.Access.t list -> (Cil.exp -> 'r, Cil.exp list -> 'r, 'r) arg_desc +val __: string -> access list -> (Cil.exp -> 'r, Cil.exp list -> 'r, 'r) arg_desc (** Argument descriptor, which captures an unnamed argument with accesses for continuation function of {!special}. *) -val __': LibraryDesc.Access.t list -> (Cil.exp -> 'r, Cil.exp list -> 'r, 'r) arg_desc +val __': access list -> (Cil.exp -> 'r, Cil.exp list -> 'r, 'r) arg_desc (** Argument descriptor, which drops (does not capture) the named argument with accesses. *) -val drop: string -> LibraryDesc.Access.t list -> ('r, 'r, 'r) arg_desc +val drop: string -> access list -> ('r, 'r, 'r) arg_desc (** Argument descriptor, which drops (does not capture) an unnamed argument with accesses. *) -val drop': LibraryDesc.Access.t list -> ('r, 'r, 'r) arg_desc +val drop': access list -> ('r, 'r, 'r) arg_desc (** Shallow {!AccessKind.Read} access. All immediate arguments of function calls are always read, this specifies the reading of pointed-to values. *) -val r: LibraryDesc.Access.t +val r: access (** Deep {!AccessKind.Read} access. All immediate arguments of function calls are always read, this specifies the reading of pointed-to values. Rarely needed. *) -val r_deep: LibraryDesc.Access.t +val r_deep: access (** Shallow {!AccessKind.Write} access. *) -val w: LibraryDesc.Access.t +val w: access (** Deep {!AccessKind.Write} access. Rarely needed. *) -val w_deep: LibraryDesc.Access.t +val w_deep: access (** Shallow {!AccessKind.Free} access. *) -val f: LibraryDesc.Access.t +val f: access (** Deep {!AccessKind.Free} access. Rarely needed. *) -val f_deep: LibraryDesc.Access.t +val f_deep: access (** Shallow {!AccessKind.Spawn} access. *) -val s: LibraryDesc.Access.t +val s: access (** Deep {!AccessKind.Spawn} access. Rarely needed. *) -val s_deep: LibraryDesc.Access.t +val s_deep: access + +(** Conditional access, e.g. on an option. *) +val if_: (unit -> bool) -> access -> access From 9ee9ce7ac842fdbc61fe9b6e6e158f7dff14c4cc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Mar 2024 15:31:46 +0200 Subject: [PATCH 030/566] Add reach_error to library functions --- src/util/library/libraryFunctions.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index 53bf804b1d..92355ca89c 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -1029,6 +1029,7 @@ let svcomp_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__VERIFIER_nondet_int", unknown []); (* declare invalidate actions to prevent invalidating globals when extern in regression tests *) ("__VERIFIER_nondet_size_t", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *) ("__VERIFIER_assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if definition missing (e.g. in evalAssert transformed output) or extraspecial *) + ("reach_error", special [] @@ Abort); (* only used if definition missing (e.g. in evalAssert transformed output) or extraspecial *) ] [@@coverage off] From 4752ddf320351654aa13e8beb831736d535a2280 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Mar 2024 15:32:03 +0200 Subject: [PATCH 031/566] Make some YAML witness validation messages more severe --- src/analyses/unassumeAnalysis.ml | 4 ++-- src/witness/yamlWitness.ml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 5895f242c9..9ec69727c0 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -252,13 +252,13 @@ struct | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _ | InvariantSet _) -> 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 + M.warn_noloc ~category:Witness "cannot unassume entry of type %s" target_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 + | Error (`Msg e) -> M.error_noloc ~category:Witness "couldn't parse entry: %s" e ) yaml_entries let emit_unassume ctx = diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index d9d39ccee1..2969997906 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -829,7 +829,7 @@ struct None | _ -> incr cnt_unsupported; - M.info_noloc ~category:Witness "cannot validate entry of type %s" target_type; + M.warn_noloc ~category:Witness "cannot validate entry of type %s" target_type; None in @@ -841,7 +841,7 @@ struct Option.to_list yaml_certificate_entry @ yaml_entry :: yaml_entries' | Error (`Msg e) -> incr cnt_error; - M.info_noloc ~category:Witness "couldn't parse entry: %s" e; + M.error_noloc ~category:Witness "couldn't parse entry: %s" e; yaml_entry :: yaml_entries' ) [] yaml_entries in From 2e22af7d31bcaaf6ca04e8337f357d2d9282aade Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Mar 2024 15:51:57 +0200 Subject: [PATCH 032/566] Add ghost_variable and ghost_update to YAML witness types --- src/witness/yamlWitness.ml | 19 ++++++++++ src/witness/yamlWitnessType.ml | 68 +++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 2969997906..213dd26f6f 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -141,6 +141,25 @@ struct }; metadata = metadata (); } + + let ghost_variable ~task ~variable ~type_ ~(initial): Entry.t = { + entry_type = GhostVariable { + variable; + scope = "global"; + type_; + initial; + }; + metadata = metadata ~task (); + } + + let ghost_update ~task ~location ~variable ~(expression): Entry.t = { + entry_type = GhostUpdate { + variable; + expression; + location; + }; + metadata = metadata ~task (); + } end let yaml_entries_to_file yaml_entries file = diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index de9fa151d8..6412c3e7b4 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -413,6 +413,60 @@ struct let entry_type = "precondition_loop_invariant_certificate" end +module GhostVariable = +struct + type t = { + variable: string; + scope: string; + type_: string; + initial: string; + } + + let entry_type = "ghost_variable" + + let to_yaml' {variable; scope; type_; initial} = + [ + ("variable", `String variable); + ("scope", `String scope); + ("type", `String type_); + ("initial", `String initial); + ] + + let of_yaml y = + let open GobYaml in + let+ variable = y |> find "variable" >>= to_string + and+ scope = y |> find "scope" >>= to_string + and+ type_ = y |> find "type" >>= to_string + and+ initial = y |> find "initial" >>= to_string in + {variable; scope; type_; initial} +end + +module GhostUpdate = +struct + type t = { + variable: string; + expression: string; + location: Location.t; + (* TODO: branching? *) + } + + let entry_type = "ghost_update" + + let to_yaml' {variable; expression; location} = + [ + ("variable", `String variable); + ("expression", `String expression); + ("location", Location.to_yaml location); + ] + + let of_yaml y = + let open GobYaml in + let+ variable = y |> find "variable" >>= to_string + and+ expression = y |> find "expression" >>= to_string + and+ location = y |> find "location" >>= Location.of_yaml in + {variable; expression; location} +end + (* TODO: could maybe use GADT, but adds ugly existential layer to entry type pattern matching *) module EntryType = struct @@ -424,6 +478,8 @@ struct | LoopInvariantCertificate of LoopInvariantCertificate.t | PreconditionLoopInvariantCertificate of PreconditionLoopInvariantCertificate.t | InvariantSet of InvariantSet.t + | GhostVariable of GhostVariable.t + | GhostUpdate of GhostUpdate.t let entry_type = function | LocationInvariant _ -> LocationInvariant.entry_type @@ -433,6 +489,8 @@ struct | LoopInvariantCertificate _ -> LoopInvariantCertificate.entry_type | PreconditionLoopInvariantCertificate _ -> PreconditionLoopInvariantCertificate.entry_type | InvariantSet _ -> InvariantSet.entry_type + | GhostVariable _ -> GhostVariable.entry_type + | GhostUpdate _ -> GhostUpdate.entry_type let to_yaml' = function | LocationInvariant x -> LocationInvariant.to_yaml' x @@ -442,6 +500,8 @@ struct | LoopInvariantCertificate x -> LoopInvariantCertificate.to_yaml' x | PreconditionLoopInvariantCertificate x -> PreconditionLoopInvariantCertificate.to_yaml' x | InvariantSet x -> InvariantSet.to_yaml' x + | GhostVariable x -> GhostVariable.to_yaml' x + | GhostUpdate x -> GhostUpdate.to_yaml' x let of_yaml y = let open GobYaml in @@ -467,8 +527,14 @@ struct else if entry_type = InvariantSet.entry_type then let+ x = y |> InvariantSet.of_yaml in InvariantSet x + else if entry_type = GhostVariable.entry_type then + let+ x = y |> GhostVariable.of_yaml in + GhostVariable x + else if entry_type = GhostUpdate.entry_type then + let+ x = y |> GhostUpdate.of_yaml in + GhostUpdate x else - Error (`Msg "entry_type") + Error (`Msg ("entry_type " ^ entry_type)) end module Entry = From 688c4dc2914897d72bcf8595286d2a49257ebef6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Mar 2024 17:07:24 +0200 Subject: [PATCH 033/566] Add mutexGhosts analysis --- src/analyses/mutexGhosts.ml | 41 +++++++++++++++++++++++++++++++++++++ src/cdomains/lockDomain.ml | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/analyses/mutexGhosts.ml diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml new file mode 100644 index 0000000000..fd5f9b5f00 --- /dev/null +++ b/src/analyses/mutexGhosts.ml @@ -0,0 +1,41 @@ +(** ([mutexGhosts]). *) + +open Analyses + + +module Spec = +struct + include UnitAnalysis.Spec + let name () = "mutexGhosts" + + module V = + struct + include Node + let is_write_only _ = true + end + + module Locked = + struct + include LockDomain.Mutexes + let name () = "locked" + end + module Unlocked = + struct + include LockDomain.Mutexes + let name () = "unlocked" + end + module G = Lattice.Prod (Locked) (Unlocked) + + let event ctx e octx = + begin match e with + | Events.Lock (l, _) -> + ctx.sideg ctx.prev_node (Locked.singleton l, Unlocked.bot ()) + | Events.Unlock l -> + ctx.sideg ctx.prev_node (Locked.bot (), Unlocked.singleton l) + | _ -> () + end; + ctx.local +end + +let _ = + MCP.register_analysis ~dep:["mutexEvents"] (module Spec : MCPSpec) diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 107c1c0692..a7b3c93571 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -8,7 +8,7 @@ module IdxDom = ValueDomain.IndexDomain open GoblintCil module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *) -module Simple = Lattice.Reverse (Mutexes) +module Simple = SetDomain.Reverse (Mutexes) module Priorities = IntDomain.Lifted module Lockset = From 45be1644d407c182fd2919b1403eb09ca3af2413 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Mar 2024 17:30:09 +0200 Subject: [PATCH 034/566] Add YamlEntryGlobal query --- src/analyses/mCP.ml | 4 ++++ src/domains/queries.ml | 9 +++++++++ src/framework/constraints.ml | 24 ++++++++++++++++++++++++ src/witness/yamlWitness.ml | 20 ++++++++++++++++++++ src/witness/yamlWitnessType.ml | 30 ++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+) diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index a3943651c0..8f66f8049d 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -283,6 +283,10 @@ struct (* InvariantGlobal is special: it only goes to corresponding analysis and the argument variant is unlifted for it *) let (n, g): V.t = Obj.obj g in f ~q:(InvariantGlobal (Obj.repr g)) (Result.top ()) (n, spec n, assoc n ctx.local) + | Queries.YamlEntryGlobal (g, task) -> + (* YamlEntryGlobal is special: it only goes to corresponding analysis and the argument variant is unlifted for it *) + let (n, g): V.t = Obj.obj g in + f ~q:(YamlEntryGlobal (Obj.repr g, task)) (Result.top ()) (n, spec n, assoc n ctx.local) | Queries.PartAccess a -> Obj.repr (access ctx a) | Queries.IterSysVars (vq, fi) -> diff --git a/src/domains/queries.ml b/src/domains/queries.ml index f5fc832a9e..cc63e5fc0d 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -63,6 +63,8 @@ type invariant_context = Invariant.context = { } [@@deriving ord, hash] +module YS = SetDomain.ToppedSet (YamlWitnessType.Entry) (struct let topname = "Top" end) + (** GADT for queries with specific result type. *) type _ t = @@ -126,6 +128,7 @@ type _ t = | MustTermAllLoops: MustBool.t t | IsEverMultiThreaded: MayBool.t t | TmpSpecial: Mval.Exp.t -> ML.t t + | YamlEntryGlobal: Obj.t * YamlWitnessType.Task.t -> YS.t t type 'a result = 'a @@ -195,6 +198,7 @@ struct | MustTermAllLoops -> (module MustBool) | IsEverMultiThreaded -> (module MayBool) | TmpSpecial _ -> (module ML) + | YamlEntryGlobal _ -> (module YS) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -263,6 +267,7 @@ struct | MustTermAllLoops -> MustBool.top () | IsEverMultiThreaded -> MayBool.top () | TmpSpecial _ -> ML.top () + | YamlEntryGlobal _ -> YS.top () end (* The type any_query can't be directly defined in Any as t, @@ -328,6 +333,7 @@ struct | Any IsEverMultiThreaded -> 55 | Any (TmpSpecial _) -> 56 | Any (IsAllocVar _) -> 57 + | Any (YamlEntryGlobal _) -> 58 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -375,6 +381,7 @@ struct | Any (WarnGlobal vi1), Any (WarnGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) | Any (Invariant i1), Any (Invariant i2) -> compare_invariant_context i1 i2 | Any (InvariantGlobal vi1), Any (InvariantGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) + | Any (YamlEntryGlobal (vi1, task1)), Any (YamlEntryGlobal (vi2, task2)) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) (* TODO: compare task *) | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MutexType m1), Any (MutexType m2) -> Mval.Unit.compare m1 m2 | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 @@ -418,6 +425,7 @@ struct | Any (Invariant i) -> hash_invariant_context i | Any (MutexType m) -> Mval.Unit.hash m | Any (InvariantGlobal vi) -> Hashtbl.hash vi + | Any (YamlEntryGlobal (vi, task)) -> Hashtbl.hash vi (* TODO: hash task *) | Any (MustProtectedVars m) -> hash_mustprotectedvars m | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start @@ -474,6 +482,7 @@ struct | Any (WarnGlobal vi) -> Pretty.dprintf "WarnGlobal _" | Any (IterSysVars _) -> Pretty.dprintf "IterSysVars _" | Any (InvariantGlobal i) -> Pretty.dprintf "InvariantGlobal _" + | Any (YamlEntryGlobal (i, task)) -> Pretty.dprintf "YamlEntryGlobal _" | Any (MutexType (v,o)) -> Pretty.dprintf "MutexType _" | Any (EvalMutexAttr a) -> Pretty.dprintf "EvalMutexAttr _" | Any MayAccessed -> Pretty.dprintf "MayAccessed" diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 52022b8aee..367386c6f1 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1250,6 +1250,14 @@ struct | `Right g -> Queries.Result.top q end + | YamlEntryGlobal (g, task) -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (YamlEntryGlobal (Obj.repr g, task)) + | `Right g -> + Queries.Result.top q + end | IterSysVars (vq, vf) -> (* vars for S *) let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in @@ -1365,6 +1373,14 @@ struct | _ -> Queries.Result.top q end + | YamlEntryGlobal (g, task) -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (YamlEntryGlobal (Obj.repr g, task)) + | _ -> + Queries.Result.top q + end | IterSysVars (vq, vf) -> (* vars for S *) let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in @@ -1650,6 +1666,14 @@ struct | `Right v -> Queries.Result.top q end + | YamlEntryGlobal (v, task) -> + let v: V.t = Obj.obj v in + begin match v with + | `Left v -> + S.query (conv ctx) (YamlEntryGlobal (Obj.repr v, task)) + | `Right v -> + Queries.Result.top q + end | _ -> S.query (conv ctx) q let branch ctx = S.branch (conv ctx) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 213dd26f6f..6ea8cc6c78 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -359,6 +359,26 @@ struct entries in + (* Generate flow-insensitive invariants *) + let entries = + if true then ( + GHT.fold (fun g v acc -> + match g with + | `Left g -> (* Spec global *) + begin match R.ask_global (YamlEntryGlobal (Obj.repr g, task)) with + | `Lifted _ as inv -> + Queries.YS.fold List.cons inv acc + | `Top -> + acc + end + | `Right _ -> (* contexts global *) + acc + ) gh entries + ) + else + entries + in + (* Generate precondition invariants. We do this in three steps: 1. Collect contexts for each function diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 6412c3e7b4..823fc993ce 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -9,6 +9,7 @@ struct command_line: string option; (* TODO: description *) } + [@@deriving eq, ord, hash] let to_yaml {name; version; command_line} = `O ([ @@ -39,6 +40,7 @@ struct language: string; specification: string option; } + [@@deriving eq, ord, hash] let to_yaml {input_files; input_file_hashes; data_model; language; specification} = `O ([ @@ -78,6 +80,7 @@ struct producer: Producer.t; task: Task.t option; } + [@@deriving eq, ord, hash] let to_yaml {format_version; uuid; creation_time; producer; task} = `O ([ @@ -111,6 +114,7 @@ struct column: int; function_: string; } + [@@deriving eq, ord, hash] let to_yaml {file_name; file_hash; line; column; function_} = `O [ @@ -138,6 +142,7 @@ struct type_: string; format: string; } + [@@deriving eq, ord, hash] let to_yaml {string; type_; format} = `O [ @@ -160,6 +165,7 @@ struct location: Location.t; loop_invariant: Invariant.t; } + [@@deriving eq, ord, hash] let entry_type = "loop_invariant" @@ -182,6 +188,7 @@ struct location: Location.t; location_invariant: Invariant.t; } + [@@deriving eq, ord, hash] let entry_type = "location_invariant" @@ -203,6 +210,7 @@ struct type t = { flow_insensitive_invariant: Invariant.t; } + [@@deriving eq, ord, hash] let entry_type = "flow_insensitive_invariant" @@ -224,6 +232,7 @@ struct loop_invariant: Invariant.t; precondition: Invariant.t; } + [@@deriving eq, ord, hash] let entry_type = "precondition_loop_invariant" @@ -251,6 +260,7 @@ struct value: string; format: string; } + [@@deriving eq, ord, hash] let invariant_type = "loop_invariant" @@ -282,6 +292,7 @@ struct type t = | LocationInvariant of LocationInvariant.t | LoopInvariant of LoopInvariant.t + [@@deriving eq, ord, hash] let invariant_type = function | LocationInvariant _ -> LocationInvariant.invariant_type @@ -309,6 +320,7 @@ struct type t = { invariant_type: InvariantType.t; } + [@@deriving eq, ord, hash] let to_yaml {invariant_type} = `O [ @@ -327,6 +339,7 @@ struct type t = { content: Invariant.t list; } + [@@deriving eq, ord, hash] let entry_type = "invariant_set" @@ -346,6 +359,7 @@ struct type_: string; file_hash: string; } + [@@deriving eq, ord, hash] let to_yaml {uuid; type_; file_hash} = `O [ @@ -369,6 +383,7 @@ struct type_: string; format: string; } + [@@deriving eq, ord, hash] let to_yaml {string; type_; format} = `O [ @@ -391,6 +406,7 @@ struct target: Target.t; certification: Certification.t; } + [@@deriving eq, ord, hash] let entry_type = "loop_invariant_certificate" @@ -421,6 +437,7 @@ struct type_: string; initial: string; } + [@@deriving eq, ord, hash] let entry_type = "ghost_variable" @@ -449,6 +466,7 @@ struct location: Location.t; (* TODO: branching? *) } + [@@deriving eq, ord, hash] let entry_type = "ghost_update" @@ -480,6 +498,7 @@ struct | InvariantSet of InvariantSet.t | GhostVariable of GhostVariable.t | GhostUpdate of GhostUpdate.t + [@@deriving eq, ord, hash] let entry_type = function | LocationInvariant _ -> LocationInvariant.entry_type @@ -539,10 +558,21 @@ end module Entry = struct + include Printable.StdLeaf + type t = { entry_type: EntryType.t; metadata: Metadata.t; } + [@@deriving eq, ord, hash] + + let name () = "YAML entry" + + let show _ = "TODO" + include Printable.SimpleShow (struct + type nonrec t = t + let show = show + end) let to_yaml {entry_type; metadata} = `O ([ From 5d3f5fe61d6d444d23deeb4ff91016689a3d8cfe Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 13 Mar 2024 17:38:02 +0200 Subject: [PATCH 035/566] Generate YAML witness ghosts for mutexes --- src/analyses/mutexGhosts.ml | 37 ++++++++++++++++++++++++++++++++++ src/witness/yamlWitness.ml | 7 ++++++- src/witness/yamlWitnessType.ml | 2 +- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index fd5f9b5f00..fe708b8cac 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -35,6 +35,43 @@ struct | _ -> () end; ctx.local + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | YamlEntryGlobal (g, task) -> + let g: V.t = Obj.obj g in + let (locked, unlocked) = ctx.global g in + let loc = Node.location g in + let location_function = (Node.find_fundec g).svar.vname in + let location = YamlWitness.Entry.location ~location:loc ~location_function in + let entries = + (* TODO: do ghost_variable-s only once *) + Locked.fold (fun l acc -> + let variable = LockDomain.Addr.show l in (* TODO: valid C name *) + let type_ = "int" in + let initial = "0" in + let entry = YamlWitness.Entry.ghost_variable ~task ~variable ~type_ ~initial in + Queries.YS.add entry acc + ) (Locked.union locked unlocked) (Queries.YS.empty ()) + in + let entries = + Locked.fold (fun l acc -> + let variable = LockDomain.Addr.show l in (* TODO: valid C name *) + let expression = "1" in + let entry = YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression in + Queries.YS.add entry acc + ) locked entries + in + let entries = + Unlocked.fold (fun l acc -> + let variable = LockDomain.Addr.show l in (* TODO: valid C name *) + let expression = "0" in + let entry = YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression in + Queries.YS.add entry acc + ) unlocked entries + in + entries + | _ -> Queries.Result.top q end let _ = diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 6ea8cc6c78..e04a4c9744 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -367,7 +367,12 @@ struct | `Left g -> (* Spec global *) begin match R.ask_global (YamlEntryGlobal (Obj.repr g, task)) with | `Lifted _ as inv -> - Queries.YS.fold List.cons inv acc + Queries.YS.fold (fun entry acc -> + if BatList.mem_cmp YamlWitnessType.Entry.compare entry acc then (* TODO: be efficient *) + acc + else + entry :: acc + ) inv acc | `Top -> acc end diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 823fc993ce..4bdb730b82 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -562,7 +562,7 @@ struct type t = { entry_type: EntryType.t; - metadata: Metadata.t; + metadata: Metadata.t [@equal fun _ _ -> true] [@compare fun _ _ -> 0] [@hash fun _ -> 1]; } [@@deriving eq, ord, hash] From a80242ae55587538f78e4ff4c3d1b5ee6601a776 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 10:52:05 +0200 Subject: [PATCH 036/566] Make protection privatization more precise with earlyglobs --- src/analyses/basePriv.ml | 4 +-- tests/regression/13-privatized/74-mutex.t | 32 +++++++++++++++++++++++ tests/regression/13-privatized/dune | 2 ++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 tests/regression/13-privatized/74-mutex.t create mode 100644 tests/regression/13-privatized/dune diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 125429231e..f08e7d710e 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -687,8 +687,8 @@ struct if not invariant then ( if not (Param.handle_atomic && ask.f MustBeAtomic) then sideg (V.unprotected x) v; (* Delay publishing unprotected write in the atomic section. *) - if !earlyglobs then (* earlyglobs workaround for 13/60 *) - sideg (V.protected x) v + if !earlyglobs && not (ThreadFlag.is_currently_multi ask) then (* earlyglobs workaround for 13/60 *) + sideg (V.protected x) v (* Also side to protected because with earlyglobs enter_multithreaded does not side everything to protected *) (* 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 Param.handle_atomic && ask.f MustBeAtomic then diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t new file mode 100644 index 0000000000..21c89cd524 --- /dev/null +++ b/tests/regression/13-privatized/74-mutex.t @@ -0,0 +1,32 @@ + $ goblint --enable ana.sv-comp.functions 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +Should also work with earlyglobs. +Earlyglobs shouldn't cause protected writes in multithreaded mode from being immediately published to protected invariant. + + $ goblint --enable ana.sv-comp.functions --enable exp.earlyglobs 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 diff --git a/tests/regression/13-privatized/dune b/tests/regression/13-privatized/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/13-privatized/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) From 086e60d9dc2c81c718e78f1b444ec63467d7bdf9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 11:51:12 +0200 Subject: [PATCH 037/566] Add ask argument to BasePriv invariant_global-s --- src/analyses/base.ml | 2 +- src/analyses/basePriv.ml | 14 +++++++------- src/analyses/basePriv.mli | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 9aca9e2079..7c6b2bf73f 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1243,7 +1243,7 @@ struct (* TODO: account for single-threaded values without earlyglobs. *) match g with | `Left g' -> (* priv *) - Priv.invariant_global (priv_getg ctx.global) g' + Priv.invariant_global (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) g' | `Right _ -> (* thread return *) Invariant.none ) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index f08e7d710e..ea46e25689 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -42,7 +42,7 @@ sig val thread_join: ?force:bool -> Q.ask -> (V.t -> G.t) -> Cil.exp -> BaseComponents (D).t -> BaseComponents (D).t val thread_return: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> ThreadIdDomain.Thread.t -> BaseComponents (D).t -> BaseComponents (D).t - val invariant_global: (V.t -> G.t) -> V.t -> Invariant.t + val invariant_global: Q.ask -> (V.t -> G.t) -> V.t -> Invariant.t val invariant_vars: Q.ask -> (V.t -> G.t) -> BaseComponents (D).t -> varinfo list val init: unit -> unit @@ -131,7 +131,7 @@ struct let thread_join ?(force=false) ask get e st = st let thread_return ask get set tid st = st - let invariant_global getg g = + let invariant_global ask getg g = ValueDomain.invariant_global getg g let invariant_vars ask getg st = [] @@ -211,7 +211,7 @@ struct let thread_join ?(force=false) ask get e st = st let thread_return ask get set tid st = st - let invariant_global getg = function + let invariant_global ask getg = function | `Right g' -> (* global *) ValueDomain.invariant_global (read_unprotected_global getg) g' | _ -> (* mutex *) @@ -621,7 +621,7 @@ struct let get_mutex_inits' = CPA.find x get_mutex_inits in VD.join get_mutex_global_x' get_mutex_inits' - let invariant_global getg = function + let invariant_global ask getg = function | `Middle g -> (* global *) ValueDomain.invariant_global (read_unprotected_global getg) g | `Left _ @@ -777,7 +777,7 @@ struct vf (V.protected g); | _ -> () - let invariant_global getg g = + let invariant_global ask getg g = match g with | `Left g' -> (* unprotected *) ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g' @@ -841,7 +841,7 @@ struct open Locksets - let invariant_global getg = function + let invariant_global ask getg = function | `Right g' -> (* global *) ValueDomain.invariant_global (fun x -> GWeak.fold (fun s' tm acc -> @@ -1633,7 +1633,7 @@ struct let threadenter ask st = time "threadenter" (Priv.threadenter ask) st let threadspawn ask get set st = time "threadspawn" (Priv.threadspawn ask get set) st let iter_sys_vars getg vq vf = time "iter_sys_vars" (Priv.iter_sys_vars getg vq) vf - let invariant_global getg v = time "invariant_global" (Priv.invariant_global getg) v + let invariant_global ask getg v = time "invariant_global" (Priv.invariant_global ask getg) v let invariant_vars ask getg st = time "invariant_vars" (Priv.invariant_vars ask getg) st let thread_join ?(force=false) ask get e st = time "thread_join" (Priv.thread_join ~force ask get e) st diff --git a/src/analyses/basePriv.mli b/src/analyses/basePriv.mli index 6906e6e4e1..e176a450fa 100644 --- a/src/analyses/basePriv.mli +++ b/src/analyses/basePriv.mli @@ -31,7 +31,7 @@ sig val thread_join: ?force:bool -> Queries.ask -> (V.t -> G.t) -> Cil.exp -> BaseDomain.BaseComponents (D).t -> BaseDomain.BaseComponents (D).t val thread_return: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> ThreadIdDomain.Thread.t -> BaseDomain.BaseComponents (D).t -> BaseDomain.BaseComponents (D).t - val invariant_global: (V.t -> G.t) -> V.t -> Invariant.t + val invariant_global: Queries.ask -> (V.t -> G.t) -> V.t -> Invariant.t (** Provides [Queries.InvariantGlobal] result for base. Should account for all unprotected/weak values of global variables. *) From b2d09da76450c1f57909247904bb828f5cf407df Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 12:07:56 +0200 Subject: [PATCH 038/566] Add MustProtectingLocks query --- src/analyses/mutexAnalysis.ml | 3 +++ src/domains/queries.ml | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 138f65ab47..1d134425f1 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -233,6 +233,9 @@ struct true else *) Mutexes.leq mutex_lockset protecting + | Queries.MustProtectingLocks g -> + let protecting = protecting ~write:true Strong g in + Mutexes.fold Queries.AD.add protecting (Queries.AD.empty ()) | Queries.MustLockset -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.fold (fun addr ls -> Queries.AD.add addr ls) held_locks (Queries.AD.empty ()) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index cc63e5fc0d..3fd8c1fc87 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -117,6 +117,7 @@ type _ t = | MustJoinedThreads: ConcDomain.MustThreadSet.t t | ThreadsJoinedCleanly: MustBool.t t | MustProtectedVars: mustprotectedvars -> VS.t t + | MustProtectingLocks: CilType.Varinfo.t -> AD.t t | Invariant: invariant_context -> Invariant.t t | InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *) | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) @@ -187,6 +188,7 @@ struct | MustJoinedThreads -> (module ConcDomain.MustThreadSet) | ThreadsJoinedCleanly -> (module MustBool) | MustProtectedVars _ -> (module VS) + | MustProtectingLocks _ -> (module AD) | Invariant _ -> (module Invariant) | InvariantGlobal _ -> (module Invariant) | WarnGlobal _ -> (module Unit) @@ -256,6 +258,7 @@ struct | MustJoinedThreads -> ConcDomain.MustThreadSet.top () | ThreadsJoinedCleanly -> MustBool.top () | MustProtectedVars _ -> VS.top () + | MustProtectingLocks _ -> AD.top () | Invariant _ -> Invariant.top () | InvariantGlobal _ -> Invariant.top () | WarnGlobal _ -> Unit.top () @@ -334,6 +337,7 @@ struct | Any (TmpSpecial _) -> 56 | Any (IsAllocVar _) -> 57 | Any (YamlEntryGlobal _) -> 58 + | Any (MustProtectingLocks _) -> 59 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -385,6 +389,7 @@ struct | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MutexType m1), Any (MutexType m2) -> Mval.Unit.compare m1 m2 | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 + | Any (MustProtectingLocks g1), Any (MustProtectingLocks g2) -> CilType.Varinfo.compare g1 g2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 @@ -427,6 +432,7 @@ struct | Any (InvariantGlobal vi) -> Hashtbl.hash vi | Any (YamlEntryGlobal (vi, task)) -> Hashtbl.hash vi (* TODO: hash task *) | Any (MustProtectedVars m) -> hash_mustprotectedvars m + | Any (MustProtectingLocks g) -> CilType.Varinfo.hash g | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv @@ -478,6 +484,7 @@ struct | Any MustJoinedThreads -> Pretty.dprintf "MustJoinedThreads" | Any ThreadsJoinedCleanly -> Pretty.dprintf "ThreadsJoinedCleanly" | Any (MustProtectedVars m) -> Pretty.dprintf "MustProtectedVars _" + | Any (MustProtectingLocks g) -> Pretty.dprintf "MustProtectingLocks _" | Any (Invariant i) -> Pretty.dprintf "Invariant _" | Any (WarnGlobal vi) -> Pretty.dprintf "WarnGlobal _" | Any (IterSysVars _) -> Pretty.dprintf "IterSysVars _" From 526d88aac4ee38d350d8a17b0691766c8dc37e31 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 12:08:31 +0200 Subject: [PATCH 039/566] Generate protected flow-insensitive invariants with ghosts --- src/analyses/basePriv.ml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index ea46e25689..1863625546 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -777,12 +777,18 @@ struct vf (V.protected g); | _ -> () - let invariant_global ask getg g = + let invariant_global (ask: Q.ask) getg g = match g with | `Left g' -> (* unprotected *) ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g' - | `Right g -> (* protected *) - Invariant.none + | `Right g' -> (* protected *) + let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) + let locks = ask.f (Q.MustProtectingLocks g') in + Q.AD.fold (fun m acc -> + let variable = LockDomain.Addr.show m in (* TODO: valid C name *) + let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in + Invariant.(acc || of_exp (Lval (Var var, NoOffset))) + ) locks inv let invariant_vars ask getg st = protected_vars ask end From d536db4708977e10d9946c68fb69effb56e69b24 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 12:10:20 +0200 Subject: [PATCH 040/566] Make mutex ghost variable names distinct from mutex variables --- src/analyses/basePriv.ml | 2 +- src/analyses/mutexGhosts.ml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 1863625546..c34808522b 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -785,7 +785,7 @@ struct let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) let locks = ask.f (Q.MustProtectingLocks g') in Q.AD.fold (fun m acc -> - let variable = LockDomain.Addr.show m in (* TODO: valid C name *) + let variable = LockDomain.Addr.show m ^ "_locked" in (* TODO: valid C name *) let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in Invariant.(acc || of_exp (Lval (Var var, NoOffset))) ) locks inv diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index fe708b8cac..1cc3cdca02 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -47,7 +47,7 @@ struct let entries = (* TODO: do ghost_variable-s only once *) Locked.fold (fun l acc -> - let variable = LockDomain.Addr.show l in (* TODO: valid C name *) + let variable = LockDomain.Addr.show l ^ "_locked" in (* TODO: valid C name *) let type_ = "int" in let initial = "0" in let entry = YamlWitness.Entry.ghost_variable ~task ~variable ~type_ ~initial in @@ -56,7 +56,7 @@ struct in let entries = Locked.fold (fun l acc -> - let variable = LockDomain.Addr.show l in (* TODO: valid C name *) + let variable = LockDomain.Addr.show l ^ "_locked" in (* TODO: valid C name *) let expression = "1" in let entry = YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression in Queries.YS.add entry acc @@ -64,7 +64,7 @@ struct in let entries = Unlocked.fold (fun l acc -> - let variable = LockDomain.Addr.show l in (* TODO: valid C name *) + let variable = LockDomain.Addr.show l ^ "_locked" in (* TODO: valid C name *) let expression = "0" in let entry = YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression in Queries.YS.add entry acc From 2eafa692f26d25897f67748a89b5c1aa9e1e9da1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 12:26:34 +0200 Subject: [PATCH 041/566] Document MutexGhosts --- src/analyses/mutexGhosts.ml | 2 +- src/goblint_lib.ml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 1cc3cdca02..b8792a2bf8 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -1,4 +1,4 @@ -(** ([mutexGhosts]). *) +(** Analysis for generating ghost variables corresponding to mutexes ([mutexGhosts]). *) open Analyses diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index e06cc8fa08..c0de408f05 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -106,6 +106,7 @@ module MutexAnalysis = MutexAnalysis module MayLocks = MayLocks module SymbLocks = SymbLocks module Deadlock = Deadlock +module MutexGhosts = MutexGhosts (** {3 Threads} From 470ddbc8490d6daaaee397288e328a3f249ca333 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 12:36:15 +0200 Subject: [PATCH 042/566] Fix coverage build --- src/analyses/basePriv.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index c34808522b..a24c686a3a 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -787,7 +787,7 @@ struct Q.AD.fold (fun m acc -> let variable = LockDomain.Addr.show m ^ "_locked" in (* TODO: valid C name *) let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in - Invariant.(acc || of_exp (Lval (Var var, NoOffset))) + Invariant.(acc || of_exp (Lval (GoblintCil.var var))) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) locks inv let invariant_vars ask getg st = protected_vars ask From 0d5ef634d8c2b0bd3adac807b4b65e726e2d03b0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 13:38:57 +0200 Subject: [PATCH 043/566] Make mutex-meet privatization more precise with earlyglobs --- src/analyses/basePriv.ml | 7 +++-- tests/regression/13-privatized/74-mutex.t | 38 +++++++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index a24c686a3a..6e9384389b 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -329,8 +329,11 @@ struct in if not invariant then ( if M.tracing then M.tracel "priv" "WRITE GLOBAL SIDE %a = %a\n" CilType.Varinfo.pretty 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. *) + let side_cpa = CPA.singleton x v in + sideg (V.global x) side_cpa; + if !earlyglobs && not (ThreadFlag.is_currently_multi ask) then + sideg V.mutex_inits side_cpa (* Also side to inits because with earlyglobs enter_multithreaded does not side everything to inits *) + (* 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 = diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index 21c89cd524..810352de44 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -1,4 +1,4 @@ - $ goblint --enable ana.sv-comp.functions 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) @@ -16,7 +16,41 @@ Should also work with earlyglobs. Earlyglobs shouldn't cause protected writes in multithreaded mode from being immediately published to protected invariant. - $ goblint --enable ana.sv-comp.functions --enable exp.earlyglobs 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable exp.earlyglobs 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +Same with mutex-meet. + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +Should also work with earlyglobs. + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable exp.earlyglobs 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) From a714dc6d512773c046a1c5dd1f937723623d0c21 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 13:47:17 +0200 Subject: [PATCH 044/566] Generate mutex-meet flow-insensitive invariants with ghosts --- src/analyses/basePriv.ml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 6e9384389b..b28381f9c6 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -294,6 +294,20 @@ module PerMutexMeetPrivBase = struct include PerMutexPrivBase + let invariant_global ask getg = function + | `Left m' as m -> (* mutex *) + let cpa = getg m in + let inv = CPA.fold (fun v _ acc -> + let inv = ValueDomain.invariant_global (fun g -> CPA.find g cpa) v in + Invariant.(acc && inv) + ) cpa Invariant.none + in + let variable = LockDomain.Addr.show m' ^ "_locked" in (* TODO: valid C name *) + let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in + Invariant.(inv || of_exp (Lval (GoblintCil.var var))) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + | g -> (* global *) + invariant_global ask getg g + let invariant_vars ask getg (st: _ BaseDomain.basecomponents_t) = (* Mutex-meet local states contain precisely the protected global variables, so we can do fewer queries than {!protected_vars}. *) From 652aeaeaca2c07f03265024ad751ca0a83e41fe5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 17:02:54 +0200 Subject: [PATCH 045/566] Add ghost variable for multithreaded mode --- src/analyses/base.ml | 11 +++++++++-- src/analyses/mutexGhosts.ml | 28 ++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7c6b2bf73f..7f73fd42af 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1237,13 +1237,20 @@ struct Invariant.none let query_invariant_global ctx g = - if GobConfig.get_bool "ana.base.invariant.enabled" && get_bool "exp.earlyglobs" then ( + if GobConfig.get_bool "ana.base.invariant.enabled" then ( (* Currently these global invariants are only sound with earlyglobs enabled for both single- and multi-threaded programs. Otherwise, the values of globals in single-threaded mode are not accounted for. *) (* TODO: account for single-threaded values without earlyglobs. *) match g with | `Left g' -> (* priv *) - Priv.invariant_global (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) g' + let inv = Priv.invariant_global (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) g' in + if get_bool "exp.earlyglobs" then + inv + else ( + let variable = "multithreaded" in + let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in + Invariant.(inv || of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType))) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) | `Right _ -> (* thread return *) Invariant.none ) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index b8792a2bf8..6a0b8d56e4 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -24,14 +24,21 @@ struct include LockDomain.Mutexes let name () = "unlocked" end - module G = Lattice.Prod (Locked) (Unlocked) + module MultiThread = + struct + include BoolDomain.MayBool + let name () = "multithread" + end + module G = Lattice.Prod3 (Locked) (Unlocked) (MultiThread) let event ctx e octx = begin match e with | Events.Lock (l, _) -> - ctx.sideg ctx.prev_node (Locked.singleton l, Unlocked.bot ()) + ctx.sideg ctx.prev_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ()) | Events.Unlock l -> - ctx.sideg ctx.prev_node (Locked.bot (), Unlocked.singleton l) + ctx.sideg ctx.prev_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ()) + | Events.EnterMultiThreaded -> + ctx.sideg ctx.prev_node (Locked.bot (), Unlocked.bot (), true) | _ -> () end; ctx.local @@ -40,7 +47,7 @@ struct match q with | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in - let (locked, unlocked) = ctx.global g in + let (locked, unlocked, multithread) = ctx.global g in let loc = Node.location g in let location_function = (Node.find_fundec g).svar.vname in let location = YamlWitness.Entry.location ~location:loc ~location_function in @@ -70,6 +77,19 @@ struct Queries.YS.add entry acc ) unlocked entries in + let entries = + if not (GobConfig.get_bool "exp.earlyglobs") && multithread then ( + let variable = "multithreaded" in + let type_ = "int" in + let initial = "0" in + let entry = YamlWitness.Entry.ghost_variable ~task ~variable ~type_ ~initial in + let expression = "1" in + let entry' = YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression in + Queries.YS.add entry (Queries.YS.add entry' entries) + ) + else + entries + in entries | _ -> Queries.Result.top q end From 7c33c72abe086f984afa16439af06518063e5ed8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 17:07:53 +0200 Subject: [PATCH 046/566] Reorder disjuncts in privatized invariants in implication order This also means that the global variable is (lazily) not accessed when the condition isn't met. --- src/analyses/base.ml | 2 +- src/analyses/basePriv.ml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 7f73fd42af..a4e5ab412a 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1249,7 +1249,7 @@ struct else ( let variable = "multithreaded" in let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in - Invariant.(inv || of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType))) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) | `Right _ -> (* thread return *) Invariant.none diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index b28381f9c6..1950c6219d 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -304,7 +304,7 @@ struct in let variable = LockDomain.Addr.show m' ^ "_locked" in (* TODO: valid C name *) let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in - Invariant.(inv || of_exp (Lval (GoblintCil.var var))) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) | g -> (* global *) invariant_global ask getg g @@ -804,7 +804,7 @@ struct Q.AD.fold (fun m acc -> let variable = LockDomain.Addr.show m ^ "_locked" in (* TODO: valid C name *) let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in - Invariant.(acc || of_exp (Lval (GoblintCil.var var))) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + Invariant.(of_exp (Lval (GoblintCil.var var)) || acc) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) locks inv let invariant_vars ask getg st = protected_vars ask From 4381e9fafa8108af5baf3d49e6e560fae0e7b7cc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 17:22:28 +0200 Subject: [PATCH 047/566] Fix MustProtectingLocks query crash with top Happened on 13-privatized/01-priv_nr --- src/analyses/basePriv.ml | 14 +++++++++----- src/analyses/mutexAnalysis.ml | 5 ++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 1950c6219d..a26ed3bac3 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -801,11 +801,15 @@ struct | `Right g' -> (* protected *) let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) let locks = ask.f (Q.MustProtectingLocks g') in - Q.AD.fold (fun m acc -> - let variable = LockDomain.Addr.show m ^ "_locked" in (* TODO: valid C name *) - let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in - Invariant.(of_exp (Lval (GoblintCil.var var)) || acc) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) locks inv + if Q.AD.is_top locks then + Invariant.none + else ( + Q.AD.fold (fun m acc -> + let variable = LockDomain.Addr.show m ^ "_locked" in (* TODO: valid C name *) + let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in + Invariant.(of_exp (Lval (GoblintCil.var var)) || acc) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) locks inv + ) let invariant_vars ask getg st = protected_vars ask end diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 1d134425f1..7e877d7dad 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -235,7 +235,10 @@ struct Mutexes.leq mutex_lockset protecting | Queries.MustProtectingLocks g -> let protecting = protecting ~write:true Strong g in - Mutexes.fold Queries.AD.add protecting (Queries.AD.empty ()) + if Mutexes.is_top protecting then + Queries.AD.top () + else + Mutexes.fold Queries.AD.add protecting (Queries.AD.empty ()) | Queries.MustLockset -> let held_locks = Lockset.export_locks (Lockset.filter snd ls) in Mutexes.fold (fun addr ls -> Queries.AD.add addr ls) held_locks (Queries.AD.empty ()) From 60a51b986900e2efa77924fb6fb83c689e6917f2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 17:27:01 +0200 Subject: [PATCH 048/566] Fix protection privatization protected invariant with no protecting mutexes Happened on 13-privatized/02-priv_rc. --- src/analyses/basePriv.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index a26ed3bac3..891e8d2183 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -799,11 +799,11 @@ struct | `Left g' -> (* unprotected *) ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g' | `Right g' -> (* protected *) - let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) let locks = ask.f (Q.MustProtectingLocks g') in - if Q.AD.is_top locks then + if Q.AD.is_top locks || Q.AD.is_empty locks then Invariant.none else ( + let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) Q.AD.fold (fun m acc -> let variable = LockDomain.Addr.show m ^ "_locked" in (* TODO: valid C name *) let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in From fd84cd95f93b82b0f062e82f5683b011a52fc09e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Mar 2024 17:31:19 +0200 Subject: [PATCH 049/566] Fix mutex-meet privatization protected invariant with no protecting mutexes Happened on 13-privatized/02-priv_rc. --- src/analyses/basePriv.ml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 891e8d2183..94f0f4092b 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -294,12 +294,15 @@ module PerMutexMeetPrivBase = struct include PerMutexPrivBase - let invariant_global ask getg = function + let invariant_global (ask: Q.ask) getg = function | `Left m' as m -> (* mutex *) let cpa = getg m in let inv = CPA.fold (fun v _ acc -> - let inv = ValueDomain.invariant_global (fun g -> CPA.find g cpa) v in - Invariant.(acc && inv) + if ask.f (MustBeProtectedBy {mutex = m'; global = v; write = true; protection = Strong}) then + let inv = ValueDomain.invariant_global (fun g -> CPA.find g cpa) v in + Invariant.(acc && inv) + else + acc ) cpa Invariant.none in let variable = LockDomain.Addr.show m' ^ "_locked" in (* TODO: valid C name *) From 516e3adc1128fef03ec49ec9bb1b03814914f462 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Mar 2024 12:31:06 +0200 Subject: [PATCH 050/566] Use RichVarinfo for witness ghost variables --- src/analyses/base.ml | 3 +-- src/analyses/basePriv.ml | 6 ++---- src/analyses/mutexGhosts.ml | 8 ++++---- src/goblint_lib.ml | 1 + src/witness/witnessGhost.ml | 21 +++++++++++++++++++++ 5 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 src/witness/witnessGhost.ml diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a4e5ab412a..7ba0d5f706 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1247,8 +1247,7 @@ struct if get_bool "exp.earlyglobs" then inv else ( - let variable = "multithreaded" in - let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in + let var = WitnessGhost.to_varinfo Multithreaded in Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) | `Right _ -> (* thread return *) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 94f0f4092b..deb603a110 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -305,8 +305,7 @@ struct acc ) cpa Invariant.none in - let variable = LockDomain.Addr.show m' ^ "_locked" in (* TODO: valid C name *) - let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in + let var = WitnessGhost.to_varinfo (Locked m') in Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) | g -> (* global *) invariant_global ask getg g @@ -808,8 +807,7 @@ struct else ( let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) Q.AD.fold (fun m acc -> - let variable = LockDomain.Addr.show m ^ "_locked" in (* TODO: valid C name *) - let var = Cilfacade.create_var (GoblintCil.makeGlobalVar variable GoblintCil.intType) in + let var = WitnessGhost.to_varinfo (Locked m) in Invariant.(of_exp (Lval (GoblintCil.var var)) || acc) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) locks inv ) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 6a0b8d56e4..aedbeac0d4 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -54,7 +54,7 @@ struct let entries = (* TODO: do ghost_variable-s only once *) Locked.fold (fun l acc -> - let variable = LockDomain.Addr.show l ^ "_locked" in (* TODO: valid C name *) + let variable = WitnessGhost.name_varinfo (Locked l) in let type_ = "int" in let initial = "0" in let entry = YamlWitness.Entry.ghost_variable ~task ~variable ~type_ ~initial in @@ -63,7 +63,7 @@ struct in let entries = Locked.fold (fun l acc -> - let variable = LockDomain.Addr.show l ^ "_locked" in (* TODO: valid C name *) + let variable = WitnessGhost.name_varinfo (Locked l) in let expression = "1" in let entry = YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression in Queries.YS.add entry acc @@ -71,7 +71,7 @@ struct in let entries = Unlocked.fold (fun l acc -> - let variable = LockDomain.Addr.show l ^ "_locked" in (* TODO: valid C name *) + let variable = WitnessGhost.name_varinfo (Locked l) in let expression = "0" in let entry = YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression in Queries.YS.add entry acc @@ -79,7 +79,7 @@ struct in let entries = if not (GobConfig.get_bool "exp.earlyglobs") && multithread then ( - let variable = "multithreaded" in + let variable = WitnessGhost.name_varinfo Multithreaded in let type_ = "int" in let initial = "0" in let entry = YamlWitness.Entry.ghost_variable ~task ~variable ~type_ ~initial in diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index c0de408f05..18a5d72aa7 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -333,6 +333,7 @@ module Graphml = Graphml module YamlWitness = YamlWitness module YamlWitnessType = YamlWitnessType +module WitnessGhost = WitnessGhost module WideningTokens = WideningTokens (** {3 Violation} diff --git a/src/witness/witnessGhost.ml b/src/witness/witnessGhost.ml new file mode 100644 index 0000000000..a9d177a569 --- /dev/null +++ b/src/witness/witnessGhost.ml @@ -0,0 +1,21 @@ +(** Ghost variables for YAML witnesses. *) + +module Var = +struct + type t = + | Locked of LockDomain.Addr.t + | Multithreaded + [@@deriving eq, hash] + + let name_varinfo = function + | Locked l -> LockDomain.Addr.show l ^ "_locked" (* TODO: valid C name *) + | Multithreaded -> "multithreaded" + + (* TODO: define correct types *) +end + +include Var + +module Map = RichVarinfo.Make (Var) + +include Map From a10c973d2712c7b36a28fb176f72fcf99117e958 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Mar 2024 12:54:52 +0200 Subject: [PATCH 051/566] Deduplicate witness ghost entry creation --- src/analyses/mutexGhosts.ml | 26 ++++++-------------------- src/witness/witnessGhost.ml | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index aedbeac0d4..0b11355d57 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -48,43 +48,29 @@ struct | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in let (locked, unlocked, multithread) = ctx.global g in - let loc = Node.location g in - let location_function = (Node.find_fundec g).svar.vname in - let location = YamlWitness.Entry.location ~location:loc ~location_function in let entries = - (* TODO: do ghost_variable-s only once *) + (* TODO: do variable_entry-s only once *) Locked.fold (fun l acc -> - let variable = WitnessGhost.name_varinfo (Locked l) in - let type_ = "int" in - let initial = "0" in - let entry = YamlWitness.Entry.ghost_variable ~task ~variable ~type_ ~initial in + let entry = WitnessGhost.variable_entry ~task (Locked l) in Queries.YS.add entry acc ) (Locked.union locked unlocked) (Queries.YS.empty ()) in let entries = Locked.fold (fun l acc -> - let variable = WitnessGhost.name_varinfo (Locked l) in - let expression = "1" in - let entry = YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression in + let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.one in Queries.YS.add entry acc ) locked entries in let entries = Unlocked.fold (fun l acc -> - let variable = WitnessGhost.name_varinfo (Locked l) in - let expression = "0" in - let entry = YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression in + let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.zero in Queries.YS.add entry acc ) unlocked entries in let entries = if not (GobConfig.get_bool "exp.earlyglobs") && multithread then ( - let variable = WitnessGhost.name_varinfo Multithreaded in - let type_ = "int" in - let initial = "0" in - let entry = YamlWitness.Entry.ghost_variable ~task ~variable ~type_ ~initial in - let expression = "1" in - let entry' = YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression in + let entry = WitnessGhost.variable_entry ~task Multithreaded in + let entry' = WitnessGhost.update_entry ~task ~node:g Multithreaded GoblintCil.one in Queries.YS.add entry (Queries.YS.add entry' entries) ) else diff --git a/src/witness/witnessGhost.ml b/src/witness/witnessGhost.ml index a9d177a569..dd4181e467 100644 --- a/src/witness/witnessGhost.ml +++ b/src/witness/witnessGhost.ml @@ -12,6 +12,14 @@ struct | Multithreaded -> "multithreaded" (* TODO: define correct types *) + + let type_ = function + | Locked _ -> GoblintCil.intType + | Multithreaded -> GoblintCil.intType + + let initial = function + | Locked _ -> GoblintCil.zero + | Multithreaded -> GoblintCil.zero end include Var @@ -19,3 +27,17 @@ include Var module Map = RichVarinfo.Make (Var) include Map + +let variable_entry ~task x = + let variable = name_varinfo x in + let type_ = String.trim (CilType.Typ.show (type_ x)) in (* CIL printer puts space at the end of some types *) + let initial = CilType.Exp.show (initial x) in + YamlWitness.Entry.ghost_variable ~task ~variable ~type_ ~initial + +let update_entry ~task ~node x e = + let loc = Node.location node in + let location_function = (Node.find_fundec node).svar.vname in + let location = YamlWitness.Entry.location ~location:loc ~location_function in + let variable = name_varinfo x in + let expression = CilType.Exp.show e in + YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression From 932ac3b312a77d09a683fa651ab114e30aec1b1e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 15 Mar 2024 13:00:59 +0200 Subject: [PATCH 052/566] Allow non-void types for RichVarinfo --- src/analyses/basePriv.ml | 2 +- src/analyses/wrapperFunctionAnalysis.ml | 2 ++ src/common/util/richVarinfo.ml | 9 +++++---- src/common/util/richVarinfo.mli | 3 ++- src/witness/witnessGhost.ml | 6 ++---- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index deb603a110..cafa84eb79 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -951,7 +951,7 @@ struct (* sync: M -> (2^M -> (G -> D)) *) include AbstractLockCenteredBase (ThreadMap) (LockCenteredBase.CPA) - let global_init_thread = RichVarinfo.single ~name:"global_init" + let global_init_thread = RichVarinfo.single ~name:"global_init" ~typ:GoblintCil.voidType let current_thread (ask: Q.ask): Thread.t = if !AnalysisState.global_initialization then ThreadIdDomain.Thread.threadinit (global_init_thread ()) ~multiple:false diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index 9510304e56..f3cf05c94d 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -144,6 +144,8 @@ module MallocWrapper : MCPSpec = struct Format.dprintf "@tid:%s" (ThreadLifted.show t) in Format.asprintf "(alloc@sid:%s%t%t)" (Node.show_id node) tid uniq_count + + let typ _ = GoblintCil.voidType end module NodeVarinfoMap = RichVarinfo.BiVarinfoMap.Make(ThreadNode) diff --git a/src/common/util/richVarinfo.ml b/src/common/util/richVarinfo.ml index d1918c40a6..6a27339eed 100644 --- a/src/common/util/richVarinfo.ml +++ b/src/common/util/richVarinfo.ml @@ -1,9 +1,9 @@ open GoblintCil -let create_var name = Cilfacade.create_var @@ makeGlobalVar name voidType +let create_var name typ = Cilfacade.create_var @@ makeGlobalVar name typ -let single ~name = - let vi = lazy (create_var name) in +let single ~name ~typ = + let vi = lazy (create_var name typ) in fun () -> Lazy.force vi @@ -21,6 +21,7 @@ module type G = sig include Hashtbl.HashedType val name_varinfo: t -> string + val typ: t -> typ end module type H = @@ -47,7 +48,7 @@ struct try XH.find !xh x with Not_found -> - let vi = create_var (X.name_varinfo x) in + let vi = create_var (X.name_varinfo x) (X.typ x) in store_f x vi; vi diff --git a/src/common/util/richVarinfo.mli b/src/common/util/richVarinfo.mli index 4e682734ee..d1c002bf84 100644 --- a/src/common/util/richVarinfo.mli +++ b/src/common/util/richVarinfo.mli @@ -2,7 +2,7 @@ open GoblintCil -val single: name:string -> (unit -> varinfo) +val single: name:string -> typ:typ -> (unit -> varinfo) module type VarinfoMap = sig @@ -18,6 +18,7 @@ module type G = sig include Hashtbl.HashedType val name_varinfo: t -> string + val typ: t -> typ end module type H = diff --git a/src/witness/witnessGhost.ml b/src/witness/witnessGhost.ml index dd4181e467..a0d25c5be6 100644 --- a/src/witness/witnessGhost.ml +++ b/src/witness/witnessGhost.ml @@ -11,9 +11,7 @@ struct | Locked l -> LockDomain.Addr.show l ^ "_locked" (* TODO: valid C name *) | Multithreaded -> "multithreaded" - (* TODO: define correct types *) - - let type_ = function + let typ = function | Locked _ -> GoblintCil.intType | Multithreaded -> GoblintCil.intType @@ -30,7 +28,7 @@ include Map let variable_entry ~task x = let variable = name_varinfo x in - let type_ = String.trim (CilType.Typ.show (type_ x)) in (* CIL printer puts space at the end of some types *) + let type_ = String.trim (CilType.Typ.show (typ x)) in (* CIL printer puts space at the end of some types *) let initial = CilType.Exp.show (initial x) in YamlWitness.Entry.ghost_variable ~task ~variable ~type_ ~initial From b0182659a02387cb57a32c67f65a4fa9331b4821 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 2 Apr 2024 12:19:24 +0300 Subject: [PATCH 053/566] Add cram test for privatized witness ghosts --- tests/regression/13-privatized/74-mutex.t | 142 +++++++++++++++++++++- 1 file changed, 140 insertions(+), 2 deletions(-) diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index 810352de44..1be888426c 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -1,4 +1,4 @@ - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) @@ -7,12 +7,81 @@ dead: 1 total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Witness] witness generation summary: + total generation entries: 9 [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 0 total memory locations: 1 + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 34 + column: 2 + function: main + - entry_type: ghost_update + variable: m_locked + expression: "1" + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 2 + function: main + - entry_type: ghost_update + variable: m_locked + expression: "1" + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 20 + column: 4 + function: producer + - entry_type: ghost_update + variable: m_locked + expression: "0" + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 38 + column: 2 + function: main + - entry_type: ghost_update + variable: m_locked + expression: "0" + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 23 + column: 4 + function: producer + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m_locked || used == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= used && used <= 1)' + type: assertion + format: C + Should also work with earlyglobs. Earlyglobs shouldn't cause protected writes in multithreaded mode from being immediately published to protected invariant. @@ -33,7 +102,7 @@ Earlyglobs shouldn't cause protected writes in multithreaded mode from being imm Same with mutex-meet. - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) @@ -42,12 +111,81 @@ Same with mutex-meet. dead: 1 total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Witness] witness generation summary: + total generation entries: 9 [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 unsafe: 0 total memory locations: 1 + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 34 + column: 2 + function: main + - entry_type: ghost_update + variable: m_locked + expression: "1" + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 2 + function: main + - entry_type: ghost_update + variable: m_locked + expression: "1" + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 20 + column: 4 + function: producer + - entry_type: ghost_update + variable: m_locked + expression: "0" + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 38 + column: 2 + function: main + - entry_type: ghost_update + variable: m_locked + expression: "0" + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 23 + column: 4 + function: producer + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m_locked || used == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= used && used <= 1)' + type: assertion + format: C + Should also work with earlyglobs. $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable exp.earlyglobs 74-mutex.c From 7992462268c057f2a26a1f5cfeeab7c0c704e5e4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 2 Apr 2024 13:42:29 +0300 Subject: [PATCH 054/566] Add cram test for witness ghosts with multiple protecting locks --- .../56-witness/64-ghost-multiple-protecting.c | 30 ++ .../56-witness/64-ghost-multiple-protecting.t | 450 ++++++++++++++++++ 2 files changed, 480 insertions(+) create mode 100644 tests/regression/56-witness/64-ghost-multiple-protecting.c create mode 100644 tests/regression/56-witness/64-ghost-multiple-protecting.t diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.c b/tests/regression/56-witness/64-ghost-multiple-protecting.c new file mode 100644 index 0000000000..0485cd124e --- /dev/null +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.c @@ -0,0 +1,30 @@ +#include + +int g1, g2; +pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m1); + pthread_mutex_lock(&m2); + g1 = 1; + g1 = 0; + pthread_mutex_unlock(&m2); + pthread_mutex_unlock(&m1); + + pthread_mutex_lock(&m1); + pthread_mutex_lock(&m2); + g2 = 1; + pthread_mutex_unlock(&m2); + pthread_mutex_lock(&m2); + g2 = 0; + pthread_mutex_unlock(&m2); + pthread_mutex_unlock(&m1); + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + return 0; +} diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.t b/tests/regression/56-witness/64-ghost-multiple-protecting.t new file mode 100644 index 0000000000..7a413332a2 --- /dev/null +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.t @@ -0,0 +1,450 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + total generation entries: 18 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + +protection doesn't have precise protected invariant for g2. + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 28 + column: 2 + function: main + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 19 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 16 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 9 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 21 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 18 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 12 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 15 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 8 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 22 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 13 + column: 2 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m2_locked + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m1_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || (m1_locked || g1 == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || (m1_locked || (0 <= g2 && g2 <= 1)))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g2 && g2 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C + + $ goblint --set ana.base.privatization protection-read --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + total generation entries: 18 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + +protection-read has precise protected invariant for g2. + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 28 + column: 2 + function: main + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 19 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 16 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 9 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 21 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 18 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 12 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 15 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 8 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 22 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 13 + column: 2 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m2_locked + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m1_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || (m1_locked || g2 == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || (m1_locked || g1 == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g2 && g2 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C + + $ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + total generation entries: 18 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 28 + column: 2 + function: main + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 19 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 16 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 9 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 21 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 18 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 12 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 15 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 8 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 22 + column: 2 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 13 + column: 2 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m2_locked + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m1_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || ((0 <= g2 && g2 <= 1) && g1 == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m1_locked || (g1 == 0 && g2 == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g2 && g2 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C From 01c9b98fc839e3591d2111b279a99765e34af6ff Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 2 Apr 2024 14:55:35 +0300 Subject: [PATCH 055/566] mutex-meet ghost invariants are maybe unsound --- .../56-witness/64-ghost-multiple-protecting.c | 11 +++ .../56-witness/64-ghost-multiple-protecting.t | 68 ++++++++++--------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.c b/tests/regression/56-witness/64-ghost-multiple-protecting.c index 0485cd124e..b19ab18ad5 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.c +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.c @@ -1,4 +1,5 @@ #include +#include int g1, g2; pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; @@ -26,5 +27,15 @@ void *t_fun(void *arg) { int main() { pthread_t id; pthread_create(&id, NULL, t_fun, NULL); + + /* pthread_mutex_lock(&m1); + __goblint_check(g1 == 0); + __goblint_check(g2 == 0); + pthread_mutex_unlock(&m1); + + pthread_mutex_lock(&m2); + __goblint_check(g1 == 0); + __goblint_check(g2 == 0); + pthread_mutex_unlock(&m2); */ return 0; } diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.t b/tests/regression/56-witness/64-ghost-multiple-protecting.t index 7a413332a2..b221d65ef1 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.t +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.t @@ -20,7 +20,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 28 + line: 29 column: 2 function: main - entry_type: ghost_update @@ -29,7 +29,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 19 + line: 20 column: 2 function: t_fun - entry_type: ghost_update @@ -38,7 +38,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 16 + line: 17 column: 2 function: t_fun - entry_type: ghost_update @@ -47,7 +47,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 9 + line: 10 column: 2 function: t_fun - entry_type: ghost_update @@ -56,7 +56,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 21 + line: 22 column: 2 function: t_fun - entry_type: ghost_update @@ -65,7 +65,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 18 + line: 19 column: 2 function: t_fun - entry_type: ghost_update @@ -74,7 +74,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 12 + line: 13 column: 2 function: t_fun - entry_type: ghost_update @@ -83,7 +83,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 15 + line: 16 column: 2 function: t_fun - entry_type: ghost_update @@ -92,7 +92,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 8 + line: 9 column: 2 function: t_fun - entry_type: ghost_update @@ -101,7 +101,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 22 + line: 23 column: 2 function: t_fun - entry_type: ghost_update @@ -110,7 +110,7 @@ protection doesn't have precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 13 + line: 14 column: 2 function: t_fun - entry_type: ghost_variable @@ -171,7 +171,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 28 + line: 29 column: 2 function: main - entry_type: ghost_update @@ -180,7 +180,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 19 + line: 20 column: 2 function: t_fun - entry_type: ghost_update @@ -189,7 +189,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 16 + line: 17 column: 2 function: t_fun - entry_type: ghost_update @@ -198,7 +198,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 9 + line: 10 column: 2 function: t_fun - entry_type: ghost_update @@ -207,7 +207,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 21 + line: 22 column: 2 function: t_fun - entry_type: ghost_update @@ -216,7 +216,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 18 + line: 19 column: 2 function: t_fun - entry_type: ghost_update @@ -225,7 +225,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 12 + line: 13 column: 2 function: t_fun - entry_type: ghost_update @@ -234,7 +234,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 15 + line: 16 column: 2 function: t_fun - entry_type: ghost_update @@ -243,7 +243,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 8 + line: 9 column: 2 function: t_fun - entry_type: ghost_update @@ -252,7 +252,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 22 + line: 23 column: 2 function: t_fun - entry_type: ghost_update @@ -261,7 +261,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 13 + line: 14 column: 2 function: t_fun - entry_type: ghost_variable @@ -313,6 +313,8 @@ protection-read has precise protected invariant for g2. unsafe: 0 total memory locations: 2 +TODO: Are the mutex-meet invariants sound? + $ yamlWitnessStrip < witness.yml - entry_type: ghost_update variable: multithreaded @@ -320,7 +322,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 28 + line: 29 column: 2 function: main - entry_type: ghost_update @@ -329,7 +331,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 19 + line: 20 column: 2 function: t_fun - entry_type: ghost_update @@ -338,7 +340,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 16 + line: 17 column: 2 function: t_fun - entry_type: ghost_update @@ -347,7 +349,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 9 + line: 10 column: 2 function: t_fun - entry_type: ghost_update @@ -356,7 +358,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 21 + line: 22 column: 2 function: t_fun - entry_type: ghost_update @@ -365,7 +367,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 18 + line: 19 column: 2 function: t_fun - entry_type: ghost_update @@ -374,7 +376,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 12 + line: 13 column: 2 function: t_fun - entry_type: ghost_update @@ -383,7 +385,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 15 + line: 16 column: 2 function: t_fun - entry_type: ghost_update @@ -392,7 +394,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 8 + line: 9 column: 2 function: t_fun - entry_type: ghost_update @@ -401,7 +403,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 22 + line: 23 column: 2 function: t_fun - entry_type: ghost_update @@ -410,7 +412,7 @@ protection-read has precise protected invariant for g2. location: file_name: 64-ghost-multiple-protecting.c file_hash: $FILE_HASH - line: 13 + line: 14 column: 2 function: t_fun - entry_type: ghost_variable From d3a5a0a3f1f864652788ed6852a019566efead3a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 4 Apr 2024 17:05:34 +0300 Subject: [PATCH 056/566] Add NOWARNs to commented out checks in 56-witness/64-ghost-multiple-protecting --- .../regression/56-witness/64-ghost-multiple-protecting.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.c b/tests/regression/56-witness/64-ghost-multiple-protecting.c index b19ab18ad5..012318ac49 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.c +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.c @@ -29,13 +29,13 @@ int main() { pthread_create(&id, NULL, t_fun, NULL); /* pthread_mutex_lock(&m1); - __goblint_check(g1 == 0); - __goblint_check(g2 == 0); + __goblint_check(g1 == 0); // NOWARN (commented out) + __goblint_check(g2 == 0); // NOWARN (commented out) pthread_mutex_unlock(&m1); pthread_mutex_lock(&m2); - __goblint_check(g1 == 0); - __goblint_check(g2 == 0); + __goblint_check(g1 == 0); // NOWARN (commented out) + __goblint_check(g2 == 0); // NOWARN (commented out) pthread_mutex_unlock(&m2); */ return 0; } From 612c1ccd4fe756af60fde77d837781fe800493ae Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 15 Apr 2024 12:37:18 +0300 Subject: [PATCH 057/566] Remove TODO about mutex-meet unsound witness invariants --- tests/regression/56-witness/64-ghost-multiple-protecting.t | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.t b/tests/regression/56-witness/64-ghost-multiple-protecting.t index b221d65ef1..619438b3e3 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.t +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.t @@ -313,8 +313,6 @@ protection-read has precise protected invariant for g2. unsafe: 0 total memory locations: 2 -TODO: Are the mutex-meet invariants sound? - $ yamlWitnessStrip < witness.yml - entry_type: ghost_update variable: multithreaded From e235ba70d1b187a0395f83729ef53f667fc41e6e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 15 Apr 2024 16:25:14 +0300 Subject: [PATCH 058/566] Rewrite mutexGhosts with may locksets per node This makes 56-witness/65-ghost-ambiguous-lock have sensible ghost updates. --- src/analyses/mutexGhosts.ml | 83 +++++---- tests/regression/13-privatized/74-mutex.c | 4 +- tests/regression/13-privatized/74-mutex.t | 20 +-- .../56-witness/64-ghost-multiple-protecting.t | 6 +- .../56-witness/65-ghost-ambiguous-lock.c | 44 +++++ .../56-witness/65-ghost-ambiguous-lock.t | 166 ++++++++++++++++++ 6 files changed, 278 insertions(+), 45 deletions(-) create mode 100644 tests/regression/56-witness/65-ghost-ambiguous-lock.c create mode 100644 tests/regression/56-witness/65-ghost-ambiguous-lock.t diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 0b11355d57..ad40915e7f 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -5,49 +5,72 @@ open Analyses module Spec = struct - include UnitAnalysis.Spec - let name () = "mutexGhosts" - - module V = + (* Copied & modified from MayLocks. *) + module Arg = struct - include Node - let is_write_only _ = true - end + module D = LockDomain.MayLocksetNoRW + module V = + struct + include Node + let is_write_only _ = true + end - module Locked = - struct - include LockDomain.Mutexes - let name () = "locked" - end - module Unlocked = - struct - include LockDomain.Mutexes - let name () = "unlocked" - end - module MultiThread = - struct - include BoolDomain.MayBool - let name () = "multithread" + module Locked = + struct + include D + let name () = "locked" + end + module MultiThread = + struct + include BoolDomain.MayBool + let name () = "multithread" + end + module G = Lattice.Prod (Locked) (MultiThread) + + let add ctx (l,r) = + D.add l ctx.local + + let remove ctx l = + match D.Addr.to_mval l with + | Some (v,o) -> + (let mtype = ctx.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) in + match mtype with + | `Lifted MutexAttrDomain.MutexKind.NonRec -> D.remove l ctx.local + | _ -> ctx.local (* we cannot remove them here *)) + | None -> ctx.local (* we cannot remove them here *) end - module G = Lattice.Prod3 (Locked) (Unlocked) (MultiThread) + + include LocksetAnalysis.MakeMay (Arg) + let name () = "mutexGhosts" + + open Arg + + let sync ctx reason = + if !AnalysisState.postsolving then + ctx.sideg ctx.prev_node (ctx.local, MultiThread.bot ()); + ctx.local let event ctx e octx = begin match e with - | Events.Lock (l, _) -> - ctx.sideg ctx.prev_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ()) - | Events.Unlock l -> - ctx.sideg ctx.prev_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ()) | Events.EnterMultiThreaded -> - ctx.sideg ctx.prev_node (Locked.bot (), Unlocked.bot (), true) + ctx.sideg ctx.prev_node (Locked.bot (), true) | _ -> () end; - ctx.local + event ctx e octx (* delegate to must lockset analysis *) let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in - let (locked, unlocked, multithread) = ctx.global g in + let module Cfg = (val !MyCFG.current_cfg) in + let next_lockset = List.fold_left (fun acc (_, next_node) -> + let (locked, _) = ctx.global next_node in + D.join acc locked + ) (D.bot ()) (Cfg.next g) + in + let (lockset, multithread) = ctx.global g in + let unlocked = D.diff lockset next_lockset in + let locked = D.diff next_lockset lockset in let entries = (* TODO: do variable_entry-s only once *) Locked.fold (fun l acc -> @@ -62,7 +85,7 @@ struct ) locked entries in let entries = - Unlocked.fold (fun l acc -> + Locked.fold (fun l acc -> let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.zero in Queries.YS.add entry acc ) unlocked entries diff --git a/tests/regression/13-privatized/74-mutex.c b/tests/regression/13-privatized/74-mutex.c index 8ed9448b7b..7c57688238 100644 --- a/tests/regression/13-privatized/74-mutex.c +++ b/tests/regression/13-privatized/74-mutex.c @@ -29,8 +29,8 @@ void* producer() int main() { pthread_t tid; - - pthread_mutex_init(&m, 0); + pthread_mutexattr_t mutexattr; pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_NORMAL); + pthread_mutex_init(&m, &mutexattr); pthread_create(&tid, 0, producer, 0); pthread_mutex_lock(&m); diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index d6d7c237e4..6f84aa184f 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -1,11 +1,11 @@ - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) [Warning][Deadcode] Logical lines of code (LLoC) summary: - live: 14 + live: 15 dead: 1 - total lines: 15 + total lines: 16 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: total generation entries: 9 @@ -90,9 +90,9 @@ Earlyglobs shouldn't cause protected writes in multithreaded mode from being imm [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) [Warning][Deadcode] Logical lines of code (LLoC) summary: - live: 14 + live: 15 dead: 1 - total lines: 15 + total lines: 16 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Race] Memory locations race summary: safe: 1 @@ -102,14 +102,14 @@ Earlyglobs shouldn't cause protected writes in multithreaded mode from being imm Same with mutex-meet. - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) [Warning][Deadcode] Logical lines of code (LLoC) summary: - live: 14 + live: 15 dead: 1 - total lines: 15 + total lines: 16 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: total generation entries: 9 @@ -193,9 +193,9 @@ Should also work with earlyglobs. [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) [Warning][Deadcode] Logical lines of code (LLoC) summary: - live: 14 + live: 15 dead: 1 - total lines: 15 + total lines: 16 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Race] Memory locations race summary: safe: 1 diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.t b/tests/regression/56-witness/64-ghost-multiple-protecting.t index 17a0a3c600..d51db2285e 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.t +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 @@ -149,7 +149,7 @@ protection doesn't have precise protected invariant for g2. type: assertion format: C - $ goblint --set ana.base.privatization protection-read --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization protection-read --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 @@ -300,7 +300,7 @@ protection-read has precise protected invariant for g2. type: assertion format: C - $ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 diff --git a/tests/regression/56-witness/65-ghost-ambiguous-lock.c b/tests/regression/56-witness/65-ghost-ambiguous-lock.c new file mode 100644 index 0000000000..b1df0ee2e8 --- /dev/null +++ b/tests/regression/56-witness/65-ghost-ambiguous-lock.c @@ -0,0 +1,44 @@ +// PARAM: --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType +#include +#include + +int g1, g2; +pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m1); + g1 = 1; + g1 = 0; + pthread_mutex_unlock(&m1); + pthread_mutex_lock(&m2); + g2 = 1; + g2 = 0; + pthread_mutex_unlock(&m2); + return NULL; +} + +void fun(pthread_mutex_t *m) { + pthread_mutex_lock(m); + // what g2 can read? + pthread_mutex_unlock(m); +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_t *m; + int r; // rand + m = r ? &m1 : &m2; + + pthread_mutex_lock(m); + // what g1 can read? + pthread_mutex_unlock(m); + + if (r) + fun(&m1); + else + fun(&m2); + return 0; +} diff --git a/tests/regression/56-witness/65-ghost-ambiguous-lock.t b/tests/regression/56-witness/65-ghost-ambiguous-lock.t new file mode 100644 index 0000000000..ee586bd531 --- /dev/null +++ b/tests/regression/56-witness/65-ghost-ambiguous-lock.t @@ -0,0 +1,166 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 65-ghost-ambiguous-lock.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 23 + dead: 0 + total lines: 23 + [Info][Witness] witness generation summary: + total generation entries: 20 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 29 + column: 3 + function: main + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 35 + column: 3 + function: main + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: fun + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 37 + column: 3 + function: main + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 24 + column: 3 + function: fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 35 + column: 3 + function: main + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: fun + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 37 + column: 3 + function: main + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 24 + column: 3 + function: fun + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m2_locked + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m1_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || g2 == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m1_locked || g1 == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g2 && g2 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C From 726f646eb85ed399955cf11d78c60db56217b9d4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 15 Apr 2024 17:49:52 +0300 Subject: [PATCH 059/566] Add test for mutex ghosts for alloc variables --- .../56-witness/66-ghost-alloc-lock.c | 37 +++++ .../56-witness/66-ghost-alloc-lock.t | 134 ++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 tests/regression/56-witness/66-ghost-alloc-lock.c create mode 100644 tests/regression/56-witness/66-ghost-alloc-lock.t diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.c b/tests/regression/56-witness/66-ghost-alloc-lock.c new file mode 100644 index 0000000000..75d405f1ab --- /dev/null +++ b/tests/regression/56-witness/66-ghost-alloc-lock.c @@ -0,0 +1,37 @@ +// PARAM: --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set ana.malloc.unique_address_count 1 +#include +#include + +int g1, g2; +pthread_mutex_t *m1; +pthread_mutex_t *m2; + +void *t_fun(void *arg) { + pthread_mutex_lock(m1); + g1 = 1; + g1 = 0; + pthread_mutex_unlock(m1); + pthread_mutex_lock(m2); + g2 = 1; + g2 = 0; + pthread_mutex_unlock(m2); + return NULL; +} + +int main() { + m1 = malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(m1, NULL); + m2 = malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(m2, NULL); + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(m1); + __goblint_check(g1 == 0); + pthread_mutex_unlock(m1); + pthread_mutex_lock(m2); + __goblint_check(g2 == 0); + pthread_mutex_unlock(m2); + return 0; +} diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.t b/tests/regression/56-witness/66-ghost-alloc-lock.t new file mode 100644 index 0000000000..84c1589317 --- /dev/null +++ b/tests/regression/56-witness/66-ghost-alloc-lock.t @@ -0,0 +1,134 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set ana.malloc.unique_address_count 1 --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 66-ghost-alloc-lock.c + [Success][Assert] Assertion "g1 == 0" will succeed (66-ghost-alloc-lock.c:31:3-31:27) + [Success][Assert] Assertion "g2 == 0" will succeed (66-ghost-alloc-lock.c:34:3-34:27) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 23 + dead: 0 + total lines: 23 + [Info][Witness] witness generation summary: + total generation entries: 16 + [Info][Race] Memory locations race summary: + safe: 4 + vulnerable: 0 + unsafe: 0 + total memory locations: 4 + +TODO: valid C names for alloc mutex ghosts + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + - entry_type: ghost_update + variable: (alloc@sid:14@tid:[main](#0))_locked + expression: "1" + location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 33 + column: 3 + function: main + - entry_type: ghost_update + variable: (alloc@sid:14@tid:[main](#0))_locked + expression: "1" + location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: (alloc@sid:14@tid:[main](#0))_locked + expression: "0" + location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 36 + column: 10 + function: main + - entry_type: ghost_update + variable: (alloc@sid:14@tid:[main](#0))_locked + expression: "0" + location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 18 + column: 10 + function: t_fun + - entry_type: ghost_update + variable: (alloc@sid:11@tid:[main](#0))_locked + expression: "1" + location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 30 + column: 3 + function: main + - entry_type: ghost_update + variable: (alloc@sid:11@tid:[main](#0))_locked + expression: "1" + location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: (alloc@sid:11@tid:[main](#0))_locked + expression: "0" + location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 36 + column: 10 + function: main + - entry_type: ghost_update + variable: (alloc@sid:11@tid:[main](#0))_locked + expression: "0" + location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 18 + column: 10 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: (alloc@sid:14@tid:[main](#0))_locked + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: (alloc@sid:11@tid:[main](#0))_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g2 && g2 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || ((alloc@sid:14@tid:[main](#0))_locked || g2 == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || ((alloc@sid:11@tid:[main](#0))_locked || g1 == 0)' + type: assertion + format: C From 3e9d7c32daffe2f818e5fa5ebf81b5a459fb40bc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 15 Apr 2024 18:05:38 +0300 Subject: [PATCH 060/566] Add valid names to alloc mutex ghosts --- src/witness/witnessGhost.ml | 10 ++++++- .../56-witness/66-ghost-alloc-lock.t | 30 +++++++++---------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/witness/witnessGhost.ml b/src/witness/witnessGhost.ml index a0d25c5be6..2aca886e78 100644 --- a/src/witness/witnessGhost.ml +++ b/src/witness/witnessGhost.ml @@ -8,7 +8,15 @@ struct [@@deriving eq, hash] let name_varinfo = function - | Locked l -> LockDomain.Addr.show l ^ "_locked" (* TODO: valid C name *) + | Locked (Addr (v, _) as l) -> + let name = + if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo v then + Printf.sprintf "alloc_%s%d" (if v.vid < 0 then "m" else "") (abs v.vid) (* turn minus into valid C name *) + else + LockDomain.Addr.show l (* TODO: valid names with interval offsets, etc *) + in + name ^ "_locked" + | Locked _ -> assert false | Multithreaded -> "multithreaded" let typ = function diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.t b/tests/regression/56-witness/66-ghost-alloc-lock.t index 84c1589317..bc3236d5a9 100644 --- a/tests/regression/56-witness/66-ghost-alloc-lock.t +++ b/tests/regression/56-witness/66-ghost-alloc-lock.t @@ -13,8 +13,6 @@ unsafe: 0 total memory locations: 4 -TODO: valid C names for alloc mutex ghosts - $ yamlWitnessStrip < witness.yml - entry_type: ghost_update variable: multithreaded @@ -26,7 +24,7 @@ TODO: valid C names for alloc mutex ghosts column: 3 function: main - entry_type: ghost_update - variable: (alloc@sid:14@tid:[main](#0))_locked + variable: alloc_m861095507_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -35,7 +33,7 @@ TODO: valid C names for alloc mutex ghosts column: 3 function: main - entry_type: ghost_update - variable: (alloc@sid:14@tid:[main](#0))_locked + variable: alloc_m861095507_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -44,7 +42,7 @@ TODO: valid C names for alloc mutex ghosts column: 3 function: t_fun - entry_type: ghost_update - variable: (alloc@sid:14@tid:[main](#0))_locked + variable: alloc_m861095507_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c @@ -53,7 +51,7 @@ TODO: valid C names for alloc mutex ghosts column: 10 function: main - entry_type: ghost_update - variable: (alloc@sid:14@tid:[main](#0))_locked + variable: alloc_m861095507_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c @@ -62,7 +60,7 @@ TODO: valid C names for alloc mutex ghosts column: 10 function: t_fun - entry_type: ghost_update - variable: (alloc@sid:11@tid:[main](#0))_locked + variable: alloc_m559918035_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -71,7 +69,7 @@ TODO: valid C names for alloc mutex ghosts column: 3 function: main - entry_type: ghost_update - variable: (alloc@sid:11@tid:[main](#0))_locked + variable: alloc_m559918035_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -80,7 +78,7 @@ TODO: valid C names for alloc mutex ghosts column: 3 function: t_fun - entry_type: ghost_update - variable: (alloc@sid:11@tid:[main](#0))_locked + variable: alloc_m559918035_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c @@ -89,7 +87,7 @@ TODO: valid C names for alloc mutex ghosts column: 10 function: main - entry_type: ghost_update - variable: (alloc@sid:11@tid:[main](#0))_locked + variable: alloc_m559918035_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c @@ -103,32 +101,32 @@ TODO: valid C names for alloc mutex ghosts type: int initial: "0" - entry_type: ghost_variable - variable: (alloc@sid:14@tid:[main](#0))_locked + variable: alloc_m861095507_locked scope: global type: int initial: "0" - entry_type: ghost_variable - variable: (alloc@sid:11@tid:[main](#0))_locked + variable: alloc_m559918035_locked scope: global type: int initial: "0" - entry_type: flow_insensitive_invariant flow_insensitive_invariant: - string: '! multithreaded || (0 <= g2 && g2 <= 1)' + string: '! multithreaded || (alloc_m861095507_locked || g2 == 0)' type: assertion format: C - entry_type: flow_insensitive_invariant flow_insensitive_invariant: - string: '! multithreaded || (0 <= g1 && g1 <= 1)' + string: '! multithreaded || (alloc_m559918035_locked || g1 == 0)' type: assertion format: C - entry_type: flow_insensitive_invariant flow_insensitive_invariant: - string: '! multithreaded || ((alloc@sid:14@tid:[main](#0))_locked || g2 == 0)' + string: '! multithreaded || (0 <= g2 && g2 <= 1)' type: assertion format: C - entry_type: flow_insensitive_invariant flow_insensitive_invariant: - string: '! multithreaded || ((alloc@sid:11@tid:[main](#0))_locked || g1 == 0)' + string: '! multithreaded || (0 <= g1 && g1 <= 1)' type: assertion format: C From b19cc2d23ae27f5692c69da724caa703a0e12660 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Apr 2024 14:01:42 +0300 Subject: [PATCH 061/566] Fix mutexGhosts indentation --- src/analyses/mutexGhosts.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index ad40915e7f..1c1a05b3b7 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -33,10 +33,11 @@ struct let remove ctx l = match D.Addr.to_mval l with | Some (v,o) -> - (let mtype = ctx.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) in - match mtype with - | `Lifted MutexAttrDomain.MutexKind.NonRec -> D.remove l ctx.local - | _ -> ctx.local (* we cannot remove them here *)) + let mtype = ctx.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) in + begin match mtype with + | `Lifted MutexAttrDomain.MutexKind.NonRec -> D.remove l ctx.local + | _ -> ctx.local (* we cannot remove them here *) + end | None -> ctx.local (* we cannot remove them here *) end From 885d0cf4b96d3aec64d90d68cd27e82867001f2a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Apr 2024 14:03:09 +0300 Subject: [PATCH 062/566] Use non-recursive mutex in 56-witness/66-ghost-alloc-lock This fixes unlock ghost update locations. --- .../56-witness/66-ghost-alloc-lock.c | 6 +-- .../56-witness/66-ghost-alloc-lock.t | 40 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.c b/tests/regression/56-witness/66-ghost-alloc-lock.c index 75d405f1ab..2c1028564a 100644 --- a/tests/regression/56-witness/66-ghost-alloc-lock.c +++ b/tests/regression/56-witness/66-ghost-alloc-lock.c @@ -18,11 +18,11 @@ void *t_fun(void *arg) { return NULL; } -int main() { +int main() { pthread_mutexattr_t attr; pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); // https://github.com/goblint/analyzer/pull/1414 m1 = malloc(sizeof(pthread_mutex_t)); - pthread_mutex_init(m1, NULL); + pthread_mutex_init(m1, &attr); m2 = malloc(sizeof(pthread_mutex_t)); - pthread_mutex_init(m2, NULL); + pthread_mutex_init(m2, &attr); pthread_t id; pthread_create(&id, NULL, t_fun, NULL); diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.t b/tests/regression/56-witness/66-ghost-alloc-lock.t index bc3236d5a9..e4d128b71e 100644 --- a/tests/regression/56-witness/66-ghost-alloc-lock.t +++ b/tests/regression/56-witness/66-ghost-alloc-lock.t @@ -24,7 +24,7 @@ column: 3 function: main - entry_type: ghost_update - variable: alloc_m861095507_locked + variable: alloc_m817990718_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -33,7 +33,7 @@ column: 3 function: main - entry_type: ghost_update - variable: alloc_m861095507_locked + variable: alloc_m817990718_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -42,25 +42,25 @@ column: 3 function: t_fun - entry_type: ghost_update - variable: alloc_m861095507_locked + variable: alloc_m817990718_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c file_hash: $FILE_HASH - line: 36 - column: 10 + line: 35 + column: 3 function: main - entry_type: ghost_update - variable: alloc_m861095507_locked + variable: alloc_m817990718_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c file_hash: $FILE_HASH - line: 18 - column: 10 + line: 17 + column: 3 function: t_fun - entry_type: ghost_update - variable: alloc_m559918035_locked + variable: alloc_m334174073_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -69,7 +69,7 @@ column: 3 function: main - entry_type: ghost_update - variable: alloc_m559918035_locked + variable: alloc_m334174073_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -78,22 +78,22 @@ column: 3 function: t_fun - entry_type: ghost_update - variable: alloc_m559918035_locked + variable: alloc_m334174073_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c file_hash: $FILE_HASH - line: 36 - column: 10 + line: 32 + column: 3 function: main - entry_type: ghost_update - variable: alloc_m559918035_locked + variable: alloc_m334174073_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c file_hash: $FILE_HASH - line: 18 - column: 10 + line: 13 + column: 3 function: t_fun - entry_type: ghost_variable variable: multithreaded @@ -101,23 +101,23 @@ type: int initial: "0" - entry_type: ghost_variable - variable: alloc_m861095507_locked + variable: alloc_m817990718_locked scope: global type: int initial: "0" - entry_type: ghost_variable - variable: alloc_m559918035_locked + variable: alloc_m334174073_locked scope: global type: int initial: "0" - entry_type: flow_insensitive_invariant flow_insensitive_invariant: - string: '! multithreaded || (alloc_m861095507_locked || g2 == 0)' + string: '! multithreaded || (alloc_m817990718_locked || g2 == 0)' type: assertion format: C - entry_type: flow_insensitive_invariant flow_insensitive_invariant: - string: '! multithreaded || (alloc_m559918035_locked || g1 == 0)' + string: '! multithreaded || (alloc_m334174073_locked || g1 == 0)' type: assertion format: C - entry_type: flow_insensitive_invariant From 21ae83a40215d181e92d31fa81b962c0937b0534 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Apr 2024 17:22:50 +0300 Subject: [PATCH 063/566] Fix mutexGhosts unlocking everything at function return --- src/analyses/mutexGhosts.ml | 21 ++++-- .../56-witness/67-ghost-no-unlock.c | 27 +++++++ .../56-witness/67-ghost-no-unlock.t | 71 +++++++++++++++++++ 3 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 tests/regression/56-witness/67-ghost-no-unlock.c create mode 100644 tests/regression/56-witness/67-ghost-no-unlock.t diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 1c1a05b3b7..934b2a0c0e 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -47,8 +47,11 @@ struct open Arg let sync ctx reason = - if !AnalysisState.postsolving then - ctx.sideg ctx.prev_node (ctx.local, MultiThread.bot ()); + if !AnalysisState.postsolving then ( + match reason with + | `Return -> ctx.sideg ctx.node (ctx.local, MultiThread.bot ()) + | _ -> ctx.sideg ctx.prev_node (ctx.local, MultiThread.bot ()) + ); ctx.local let event ctx e octx = @@ -64,12 +67,16 @@ struct | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in let module Cfg = (val !MyCFG.current_cfg) in - let next_lockset = List.fold_left (fun acc (_, next_node) -> - let (locked, _) = ctx.global next_node in - D.join acc locked - ) (D.bot ()) (Cfg.next g) - in let (lockset, multithread) = ctx.global g in + let next_lockset = + match Cfg.next g with + | [] -> lockset (* HACK for return nodes *) + | nexts -> + List.fold_left (fun acc (_, next_node) -> + let (locked, _) = ctx.global next_node in + D.join acc locked + ) (D.bot ()) nexts + in let unlocked = D.diff lockset next_lockset in let locked = D.diff next_lockset lockset in let entries = diff --git a/tests/regression/56-witness/67-ghost-no-unlock.c b/tests/regression/56-witness/67-ghost-no-unlock.c new file mode 100644 index 0000000000..fc10b919d0 --- /dev/null +++ b/tests/regression/56-witness/67-ghost-no-unlock.c @@ -0,0 +1,27 @@ +// PARAM: --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType +#include +#include + +int g1; +pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m1); + g1 = 1; + g1 = 0; + pthread_mutex_unlock(&m1); + return NULL; +} + +int main() { + + + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m1); + __goblint_check(g1 == 0); + // no unlock + return 0; // there should be no ghost updates for unlock here +} diff --git a/tests/regression/56-witness/67-ghost-no-unlock.t b/tests/regression/56-witness/67-ghost-no-unlock.t new file mode 100644 index 0000000000..491dd9cf44 --- /dev/null +++ b/tests/regression/56-witness/67-ghost-no-unlock.t @@ -0,0 +1,71 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 67-ghost-no-unlock.c + [Success][Assert] Assertion "g1 == 0" will succeed (67-ghost-no-unlock.c:24:3-24:27) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Witness] witness generation summary: + total generation entries: 8 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 21 + column: 3 + function: main + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 9 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 12 + column: 3 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m1_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m1_locked || g1 == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C From d67c083ba9755e6d1ecc38daaf28d3a266a1ee38 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 18 Apr 2024 14:49:58 +0300 Subject: [PATCH 064/566] Revert "Rewrite mutexGhosts with may locksets per node" This partially reverts commits e235ba70d1b187a0395f83729ef53f667fc41e6e and 21ae83a40215d181e92d31fa81b962c0937b0534. --- src/analyses/mutexGhosts.ml | 91 ++++++++++++------------------------- 1 file changed, 30 insertions(+), 61 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 934b2a0c0e..0b11355d57 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -5,80 +5,49 @@ open Analyses module Spec = struct - (* Copied & modified from MayLocks. *) - module Arg = - struct - module D = LockDomain.MayLocksetNoRW - module V = - struct - include Node - let is_write_only _ = true - end - - module Locked = - struct - include D - let name () = "locked" - end - module MultiThread = - struct - include BoolDomain.MayBool - let name () = "multithread" - end - module G = Lattice.Prod (Locked) (MultiThread) - - let add ctx (l,r) = - D.add l ctx.local - - let remove ctx l = - match D.Addr.to_mval l with - | Some (v,o) -> - let mtype = ctx.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) in - begin match mtype with - | `Lifted MutexAttrDomain.MutexKind.NonRec -> D.remove l ctx.local - | _ -> ctx.local (* we cannot remove them here *) - end - | None -> ctx.local (* we cannot remove them here *) - end - - include LocksetAnalysis.MakeMay (Arg) + include UnitAnalysis.Spec let name () = "mutexGhosts" - open Arg + module V = + struct + include Node + let is_write_only _ = true + end - let sync ctx reason = - if !AnalysisState.postsolving then ( - match reason with - | `Return -> ctx.sideg ctx.node (ctx.local, MultiThread.bot ()) - | _ -> ctx.sideg ctx.prev_node (ctx.local, MultiThread.bot ()) - ); - ctx.local + module Locked = + struct + include LockDomain.Mutexes + let name () = "locked" + end + module Unlocked = + struct + include LockDomain.Mutexes + let name () = "unlocked" + end + module MultiThread = + struct + include BoolDomain.MayBool + let name () = "multithread" + end + module G = Lattice.Prod3 (Locked) (Unlocked) (MultiThread) let event ctx e octx = begin match e with + | Events.Lock (l, _) -> + ctx.sideg ctx.prev_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ()) + | Events.Unlock l -> + ctx.sideg ctx.prev_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ()) | Events.EnterMultiThreaded -> - ctx.sideg ctx.prev_node (Locked.bot (), true) + ctx.sideg ctx.prev_node (Locked.bot (), Unlocked.bot (), true) | _ -> () end; - event ctx e octx (* delegate to must lockset analysis *) + ctx.local let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in - let module Cfg = (val !MyCFG.current_cfg) in - let (lockset, multithread) = ctx.global g in - let next_lockset = - match Cfg.next g with - | [] -> lockset (* HACK for return nodes *) - | nexts -> - List.fold_left (fun acc (_, next_node) -> - let (locked, _) = ctx.global next_node in - D.join acc locked - ) (D.bot ()) nexts - in - let unlocked = D.diff lockset next_lockset in - let locked = D.diff next_lockset lockset in + let (locked, unlocked, multithread) = ctx.global g in let entries = (* TODO: do variable_entry-s only once *) Locked.fold (fun l acc -> @@ -93,7 +62,7 @@ struct ) locked entries in let entries = - Locked.fold (fun l acc -> + Unlocked.fold (fun l acc -> let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.zero in Queries.YS.add entry acc ) unlocked entries From c472adff1fdf72a6da6142d07f55e59069f91aeb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 18 Apr 2024 18:00:05 +0300 Subject: [PATCH 065/566] Add lock global unknowns to mutexGhosts --- src/analyses/mutexGhosts.ml | 92 +++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 0b11355d57..d80291532f 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -10,8 +10,12 @@ struct module V = struct - include Node - let is_write_only _ = true + include Printable.Either (Node) (LockDomain.Addr) + let node x = `Left x + let lock x = `Right x + let is_write_only = function + | `Left _ -> false + | `Right _ -> true end module Locked = @@ -29,16 +33,29 @@ struct include BoolDomain.MayBool let name () = "multithread" end - module G = Lattice.Prod3 (Locked) (Unlocked) (MultiThread) + module G = + struct + include Lattice.Lift2 (Lattice.Prod3 (Locked) (Unlocked) (MultiThread)) (Lattice.Unit) + let node = function + | `Bot -> (Locked.bot (), Unlocked.bot (), MultiThread.bot ()) + | `Lifted1 x -> x + | _ -> failwith "MutexGhosts.node" + let lock = function + | `Bot -> Lattice.Unit.bot () + | `Lifted2 x -> x + | _ -> failwith "MutexGhosts.lock" + let create_node node = `Lifted1 node + let create_lock lock = `Lifted2 lock + end let event ctx e octx = begin match e with | Events.Lock (l, _) -> - ctx.sideg ctx.prev_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ()) + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ())) | Events.Unlock l -> - ctx.sideg ctx.prev_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ()) + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ())) | Events.EnterMultiThreaded -> - ctx.sideg ctx.prev_node (Locked.bot (), Unlocked.bot (), true) + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true)) | _ -> () end; ctx.local @@ -47,36 +64,41 @@ struct match q with | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in - let (locked, unlocked, multithread) = ctx.global g in - let entries = - (* TODO: do variable_entry-s only once *) - Locked.fold (fun l acc -> - let entry = WitnessGhost.variable_entry ~task (Locked l) in - Queries.YS.add entry acc - ) (Locked.union locked unlocked) (Queries.YS.empty ()) - in - let entries = - Locked.fold (fun l acc -> - let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.one in - Queries.YS.add entry acc - ) locked entries - in - let entries = - Unlocked.fold (fun l acc -> - let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.zero in - Queries.YS.add entry acc - ) unlocked entries - in - let entries = - if not (GobConfig.get_bool "exp.earlyglobs") && multithread then ( - let entry = WitnessGhost.variable_entry ~task Multithreaded in - let entry' = WitnessGhost.update_entry ~task ~node:g Multithreaded GoblintCil.one in - Queries.YS.add entry (Queries.YS.add entry' entries) - ) - else + begin match g with + | `Left g' -> + let (locked, unlocked, multithread) = G.node (ctx.global g) in + let g = g' in + let entries = + (* TODO: do variable_entry-s only once *) + Locked.fold (fun l acc -> + let entry = WitnessGhost.variable_entry ~task (Locked l) in + Queries.YS.add entry acc + ) (Locked.union locked unlocked) (Queries.YS.empty ()) + in + let entries = + Locked.fold (fun l acc -> + let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.one in + Queries.YS.add entry acc + ) locked entries + in + let entries = + Unlocked.fold (fun l acc -> + let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.zero in + Queries.YS.add entry acc + ) unlocked entries + in + let entries = + if not (GobConfig.get_bool "exp.earlyglobs") && multithread then ( + let entry = WitnessGhost.variable_entry ~task Multithreaded in + let entry' = WitnessGhost.update_entry ~task ~node:g Multithreaded GoblintCil.one in + Queries.YS.add entry (Queries.YS.add entry' entries) + ) + else + entries + in entries - in - entries + | `Right _ -> assert false + end | _ -> Queries.Result.top q end From fd64898163e98115a3d3dfd253856005796c68a9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 Apr 2024 13:34:37 +0300 Subject: [PATCH 066/566] Add PARAM to 56-witness/64-ghost-multiple-protecting --- tests/regression/56-witness/64-ghost-multiple-protecting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.c b/tests/regression/56-witness/64-ghost-multiple-protecting.c index 012318ac49..589aa92bff 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.c +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.c @@ -1,6 +1,6 @@ +// PARAM: --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType #include #include - int g1, g2; pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER; From e3ded4e20de4790baf9d9bf5b6fad2904d3ae598 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 Apr 2024 13:34:55 +0300 Subject: [PATCH 067/566] Find ambiguous mutexes in mutexGhosts --- src/analyses/mutexGhosts.ml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index d80291532f..083763f41b 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -35,7 +35,7 @@ struct end module G = struct - include Lattice.Lift2 (Lattice.Prod3 (Locked) (Unlocked) (MultiThread)) (Lattice.Unit) + include Lattice.Lift2 (Lattice.Prod3 (Locked) (Unlocked) (MultiThread)) (BoolDomain.MayBool) let node = function | `Bot -> (Locked.bot (), Unlocked.bot (), MultiThread.bot ()) | `Lifted1 x -> x @@ -51,9 +51,25 @@ struct let event ctx e octx = begin match e with | Events.Lock (l, _) -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ())) + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ())); + if !AnalysisState.postsolving then ( + let (locked, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in + if Locked.cardinal locked > 1 then ( + Locked.iter (fun lock -> + ctx.sideg (V.lock lock) (G.create_lock true) + ) locked + ); + ) | Events.Unlock l -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ())) + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ())); + if !AnalysisState.postsolving then ( + let (_, unlocked, _) = G.node (ctx.global (V.node ctx.prev_node)) in + if Locked.cardinal unlocked > 1 then ( + Locked.iter (fun lock -> + ctx.sideg (V.lock lock) (G.create_lock true) + ) unlocked + ); + ) | Events.EnterMultiThreaded -> ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true)) | _ -> () @@ -97,7 +113,7 @@ struct entries in entries - | `Right _ -> assert false + | `Right _ -> Queries.Result.top q end | _ -> Queries.Result.top q end From b96f8a210c18fa6f1380f29d90fe53f7ad77efbe Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 Apr 2024 13:50:04 +0300 Subject: [PATCH 068/566] Avoid emitting witness ghosts for ambiguous mutexes --- src/analyses/base.ml | 5 +- src/analyses/basePriv.ml | 10 +- src/analyses/mutexGhosts.ml | 42 ++++-- src/domains/queries.ml | 7 + src/witness/witnessGhost.ml | 4 +- .../56-witness/65-ghost-ambiguous-lock.t | 130 +----------------- 6 files changed, 55 insertions(+), 143 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4cc5c51262..ac31e29163 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1248,7 +1248,10 @@ struct inv else ( let var = WitnessGhost.to_varinfo Multithreaded in - Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + if ctx.ask (GhostVarAvailable var) then + Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + else + Invariant.none ) | `Right _ -> (* thread return *) Invariant.none diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 832aaf54c1..129d0d5d69 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -343,7 +343,10 @@ struct ) cpa Invariant.none in let var = WitnessGhost.to_varinfo (Locked m') in - Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + if ask.f (GhostVarAvailable var) then + Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + else + Invariant.none | g -> (* global *) invariant_global ask getg g @@ -859,7 +862,10 @@ struct let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) Q.AD.fold (fun m acc -> let var = WitnessGhost.to_varinfo (Locked m) in - Invariant.(of_exp (Lval (GoblintCil.var var)) || acc) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + if ask.f (GhostVarAvailable var) then + Invariant.(of_exp (Lval (GoblintCil.var var)) || acc) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + else + Invariant.none ) locks inv ) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 083763f41b..21a12db7a1 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -41,7 +41,7 @@ struct | `Lifted1 x -> x | _ -> failwith "MutexGhosts.node" let lock = function - | `Bot -> Lattice.Unit.bot () + | `Bot -> BoolDomain.MayBool.bot () | `Lifted2 x -> x | _ -> failwith "MutexGhosts.lock" let create_node node = `Lifted1 node @@ -76,8 +76,14 @@ struct end; ctx.local + let ghost_var_available ctx = function + | WitnessGhost.Var.Locked lock -> not (G.lock (ctx.global (V.lock lock)): bool) + | Multithreaded -> true + let query ctx (type a) (q: a Queries.t): a Queries.result = match q with + | GhostVarAvailable vi -> + GobOption.exists (ghost_var_available ctx) (WitnessGhost.from_varinfo vi) | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in begin match g with @@ -87,27 +93,43 @@ struct let entries = (* TODO: do variable_entry-s only once *) Locked.fold (fun l acc -> - let entry = WitnessGhost.variable_entry ~task (Locked l) in - Queries.YS.add entry acc + if ghost_var_available ctx (Locked l) then ( + let entry = WitnessGhost.variable_entry ~task (Locked l) in + Queries.YS.add entry acc + ) + else + acc ) (Locked.union locked unlocked) (Queries.YS.empty ()) in let entries = Locked.fold (fun l acc -> - let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.one in - Queries.YS.add entry acc + if ghost_var_available ctx (Locked l) then ( + let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.one in + Queries.YS.add entry acc + ) + else + acc ) locked entries in let entries = Unlocked.fold (fun l acc -> - let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.zero in - Queries.YS.add entry acc + if ghost_var_available ctx (Locked l) then ( + let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.zero in + Queries.YS.add entry acc + ) + else + acc ) unlocked entries in let entries = if not (GobConfig.get_bool "exp.earlyglobs") && multithread then ( - let entry = WitnessGhost.variable_entry ~task Multithreaded in - let entry' = WitnessGhost.update_entry ~task ~node:g Multithreaded GoblintCil.one in - Queries.YS.add entry (Queries.YS.add entry' entries) + if ghost_var_available ctx Multithreaded then ( + let entry = WitnessGhost.variable_entry ~task Multithreaded in + let entry' = WitnessGhost.update_entry ~task ~node:g Multithreaded GoblintCil.one in + Queries.YS.add entry (Queries.YS.add entry' entries) + ) + else + entries ) else entries diff --git a/src/domains/queries.ml b/src/domains/queries.ml index b9b13a7584..9a50af1907 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -130,6 +130,7 @@ type _ t = | TmpSpecial: Mval.Exp.t -> ML.t t | MaySignedOverflow: exp -> MayBool.t t | YamlEntryGlobal: Obj.t * YamlWitnessType.Task.t -> YS.t t + | GhostVarAvailable: varinfo -> MustBool.t t type 'a result = 'a @@ -202,6 +203,7 @@ struct | TmpSpecial _ -> (module ML) | MaySignedOverflow _ -> (module MayBool) | YamlEntryGlobal _ -> (module YS) + | GhostVarAvailable _ -> (module MustBool) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -273,6 +275,7 @@ struct | TmpSpecial _ -> ML.top () | MaySignedOverflow _ -> MayBool.top () | YamlEntryGlobal _ -> YS.top () + | GhostVarAvailable _ -> MustBool.top () end (* The type any_query can't be directly defined in Any as t, @@ -341,6 +344,7 @@ struct | Any (MaySignedOverflow _) -> 58 | Any (YamlEntryGlobal _) -> 59 | Any (MustProtectingLocks _) -> 60 + | Any (GhostVarAvailable _) -> 61 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -397,6 +401,7 @@ struct | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 | Any (MaySignedOverflow e1), Any (MaySignedOverflow e2) -> CilType.Exp.compare e1 e2 + | Any (GhostVarAvailable vi1), Any (GhostVarAvailable vi2) -> CilType.Varinfo.compare vi1 vi2 (* only argumentless queries should remain *) | _, _ -> Stdlib.compare (order a) (order b) @@ -441,6 +446,7 @@ struct | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv | Any (MaySignedOverflow e) -> CilType.Exp.hash e + | Any (GhostVarAvailable vi) -> CilType.Varinfo.hash vi (* IterSysVars: *) (* - argument is a function and functions cannot be compared in any meaningful way. *) (* - doesn't matter because IterSysVars is always queried from outside of the analysis, so MCP's query caching is not done for it. *) @@ -506,6 +512,7 @@ struct | Any IsEverMultiThreaded -> Pretty.dprintf "IsEverMultiThreaded" | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv | Any (MaySignedOverflow e) -> Pretty.dprintf "MaySignedOverflow %a" CilType.Exp.pretty e + | Any (GhostVarAvailable vi) -> Pretty.dprintf "GhostVarAvailable %a" CilType.Varinfo.pretty vi end let to_value_domain_ask (ask: ask) = diff --git a/src/witness/witnessGhost.ml b/src/witness/witnessGhost.ml index 2aca886e78..010f450954 100644 --- a/src/witness/witnessGhost.ml +++ b/src/witness/witnessGhost.ml @@ -19,6 +19,8 @@ struct | Locked _ -> assert false | Multithreaded -> "multithreaded" + let describe_varinfo _ _ = "" + let typ = function | Locked _ -> GoblintCil.intType | Multithreaded -> GoblintCil.intType @@ -30,7 +32,7 @@ end include Var -module Map = RichVarinfo.Make (Var) +module Map = RichVarinfo.BiVarinfoMap.Make (Var) include Map diff --git a/tests/regression/56-witness/65-ghost-ambiguous-lock.t b/tests/regression/56-witness/65-ghost-ambiguous-lock.t index ee586bd531..708e27ca64 100644 --- a/tests/regression/56-witness/65-ghost-ambiguous-lock.t +++ b/tests/regression/56-witness/65-ghost-ambiguous-lock.t @@ -4,7 +4,7 @@ dead: 0 total lines: 23 [Info][Witness] witness generation summary: - total generation entries: 20 + total generation entries: 4 [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 @@ -21,139 +21,11 @@ line: 29 column: 3 function: main - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 35 - column: 3 - function: main - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 22 - column: 3 - function: fun - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 14 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 37 - column: 3 - function: main - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 24 - column: 3 - function: fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 17 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 35 - column: 3 - function: main - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 22 - column: 3 - function: fun - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 10 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 37 - column: 3 - function: main - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 24 - column: 3 - function: fun - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 13 - column: 3 - function: t_fun - entry_type: ghost_variable variable: multithreaded scope: global type: int initial: "0" - - entry_type: ghost_variable - variable: m2_locked - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m1_locked - scope: global - type: int - initial: "0" - - entry_type: flow_insensitive_invariant - flow_insensitive_invariant: - string: '! multithreaded || (m2_locked || g2 == 0)' - type: assertion - format: C - - entry_type: flow_insensitive_invariant - flow_insensitive_invariant: - string: '! multithreaded || (m1_locked || g1 == 0)' - type: assertion - format: C - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (0 <= g2 && g2 <= 1)' From c6f12a60491f6650fcb07ba01dd54344a4a30fe3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 Apr 2024 13:57:06 +0300 Subject: [PATCH 069/566] Move LockDomain.Symbolic to SymbLocksDomain --- src/analyses/symbLocks.ml | 4 +-- src/cdomains/lockDomain.ml | 50 -------------------------------- src/cdomains/symbLocksDomain.ml | 51 +++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/analyses/symbLocks.ml b/src/analyses/symbLocks.ml index 6fd18de6ff..b1727ace81 100644 --- a/src/analyses/symbLocks.ml +++ b/src/analyses/symbLocks.ml @@ -23,8 +23,8 @@ struct exception Top - module D = LockDomain.Symbolic - module C = LockDomain.Symbolic + module D = SymbLocksDomain.Symbolic + module C = SymbLocksDomain.Symbolic let name () = "symb_locks" diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index b22931001b..5aaa441428 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -74,53 +74,3 @@ module MayLocksetNoRW = struct include PreValueDomain.AD end - -module Symbolic = -struct - (* TODO: use SetDomain.Reverse *) - module S = SetDomain.ToppedSet (Exp) (struct let topname = "All mutexes" end) - include Lattice.Reverse (S) - - let rec eq_set (ask: Queries.ask) e = - S.union - (match ask.f (Queries.EqualSet e) with - | es when not (Queries.ES.is_bot es) -> - Queries.ES.fold S.add es (S.empty ()) - | _ -> S.empty ()) - (match e with - | SizeOf _ - | SizeOfE _ - | SizeOfStr _ - | AlignOf _ - | Const _ - | AlignOfE _ - | UnOp _ - | BinOp _ - | Question _ - | Real _ - | Imag _ - | AddrOfLabel _ -> S.empty () - | AddrOf (Var _,_) - | StartOf (Var _,_) - | Lval (Var _,_) -> S.singleton e - | AddrOf (Mem e,ofs) -> S.map (fun e -> AddrOf (Mem e,ofs)) (eq_set ask e) - | StartOf (Mem e,ofs) -> S.map (fun e -> StartOf (Mem e,ofs)) (eq_set ask e) - | Lval (Mem e,ofs) -> S.map (fun e -> Lval (Mem e,ofs)) (eq_set ask e) - | CastE (_,e) -> eq_set ask e - ) - - let add (ask: Queries.ask) e st = - let no_casts = S.map Expcompare.stripCastsDeepForPtrArith (eq_set ask e) in - let addrs = S.filter (function AddrOf _ -> true | _ -> false) no_casts in - S.union addrs st - let remove ask e st = - (* TODO: Removing based on must-equality sets is not sound! *) - let no_casts = S.map Expcompare.stripCastsDeepForPtrArith (eq_set ask e) in - let addrs = S.filter (function AddrOf _ -> true | _ -> false) no_casts in - S.diff st addrs - let remove_var v st = S.filter (fun x -> not (SymbLocksDomain.Exp.contains_var v x)) st - - let filter = S.filter - let fold = S.fold - -end diff --git a/src/cdomains/symbLocksDomain.ml b/src/cdomains/symbLocksDomain.ml index ba2b96e8d0..bb260ad412 100644 --- a/src/cdomains/symbLocksDomain.ml +++ b/src/cdomains/symbLocksDomain.ml @@ -317,3 +317,54 @@ struct let of_mval (v, o) = of_mval (v, conv_const_offset o) end + + +module Symbolic = +struct + (* TODO: use SetDomain.Reverse *) + module S = SetDomain.ToppedSet (Exp) (struct let topname = "All mutexes" end) + include Lattice.Reverse (S) + + let rec eq_set (ask: Queries.ask) e = + S.union + (match ask.f (Queries.EqualSet e) with + | es when not (Queries.ES.is_bot es) -> + Queries.ES.fold S.add es (S.empty ()) + | _ -> S.empty ()) + (match e with + | SizeOf _ + | SizeOfE _ + | SizeOfStr _ + | AlignOf _ + | Const _ + | AlignOfE _ + | UnOp _ + | BinOp _ + | Question _ + | Real _ + | Imag _ + | AddrOfLabel _ -> S.empty () + | AddrOf (Var _,_) + | StartOf (Var _,_) + | Lval (Var _,_) -> S.singleton e + | AddrOf (Mem e,ofs) -> S.map (fun e -> AddrOf (Mem e,ofs)) (eq_set ask e) + | StartOf (Mem e,ofs) -> S.map (fun e -> StartOf (Mem e,ofs)) (eq_set ask e) + | Lval (Mem e,ofs) -> S.map (fun e -> Lval (Mem e,ofs)) (eq_set ask e) + | CastE (_,e) -> eq_set ask e + ) + + let add (ask: Queries.ask) e st = + let no_casts = S.map Expcompare.stripCastsDeepForPtrArith (eq_set ask e) in + let addrs = S.filter (function AddrOf _ -> true | _ -> false) no_casts in + S.union addrs st + let remove ask e st = + (* TODO: Removing based on must-equality sets is not sound! *) + let no_casts = S.map Expcompare.stripCastsDeepForPtrArith (eq_set ask e) in + let addrs = S.filter (function AddrOf _ -> true | _ -> false) no_casts in + S.diff st addrs + let remove_var v st = S.filter (fun x -> not (Exp.contains_var v x)) st + + let filter = S.filter + let fold = S.fold + +end From 8985d64633a0b3d0f6812df8ee784c87f332f499 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 Apr 2024 13:57:37 +0300 Subject: [PATCH 070/566] Extract WitnessGhostVar to break dependency cycle --- src/witness/witnessGhost.ml | 30 +---------------------------- src/witness/witnessGhostVar.ml | 35 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 29 deletions(-) create mode 100644 src/witness/witnessGhostVar.ml diff --git a/src/witness/witnessGhost.ml b/src/witness/witnessGhost.ml index 010f450954..cdd26b36aa 100644 --- a/src/witness/witnessGhost.ml +++ b/src/witness/witnessGhost.ml @@ -1,34 +1,6 @@ (** Ghost variables for YAML witnesses. *) -module Var = -struct - type t = - | Locked of LockDomain.Addr.t - | Multithreaded - [@@deriving eq, hash] - - let name_varinfo = function - | Locked (Addr (v, _) as l) -> - let name = - if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo v then - Printf.sprintf "alloc_%s%d" (if v.vid < 0 then "m" else "") (abs v.vid) (* turn minus into valid C name *) - else - LockDomain.Addr.show l (* TODO: valid names with interval offsets, etc *) - in - name ^ "_locked" - | Locked _ -> assert false - | Multithreaded -> "multithreaded" - - let describe_varinfo _ _ = "" - - let typ = function - | Locked _ -> GoblintCil.intType - | Multithreaded -> GoblintCil.intType - - let initial = function - | Locked _ -> GoblintCil.zero - | Multithreaded -> GoblintCil.zero -end +module Var = WitnessGhostVar include Var diff --git a/src/witness/witnessGhostVar.ml b/src/witness/witnessGhostVar.ml new file mode 100644 index 0000000000..afcf5d4dba --- /dev/null +++ b/src/witness/witnessGhostVar.ml @@ -0,0 +1,35 @@ +(** Ghost variables for YAML witnesses. *) + +type t = + | Locked of LockDomain.Addr.t + | Multithreaded +[@@deriving eq, ord, hash] + +let name_varinfo = function + | Locked (Addr (v, _) as l) -> + let name = + if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo v then + Printf.sprintf "alloc_%s%d" (if v.vid < 0 then "m" else "") (abs v.vid) (* turn minus into valid C name *) + else + LockDomain.Addr.show l (* TODO: valid names with interval offsets, etc *) + in + name ^ "_locked" + | Locked _ -> assert false + | Multithreaded -> "multithreaded" + +let show = name_varinfo + +include Printable.SimpleShow (struct + type nonrec t = t + let show = show + end) + +let describe_varinfo _ _ = "" + +let typ = function + | Locked _ -> GoblintCil.intType + | Multithreaded -> GoblintCil.intType + +let initial = function + | Locked _ -> GoblintCil.zero + | Multithreaded -> GoblintCil.zero From b04af51f2d86bb20d7908a72948fd97275fdce14 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 Apr 2024 13:58:55 +0300 Subject: [PATCH 071/566] Refactor GhostVarAvailable query --- src/analyses/base.ml | 5 +++-- src/analyses/basePriv.ml | 10 ++++++---- src/analyses/mutexGhosts.ml | 3 +-- src/domains/queries.ml | 12 ++++++------ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index ac31e29163..c1001f8b80 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1247,9 +1247,10 @@ struct if get_bool "exp.earlyglobs" then inv else ( - let var = WitnessGhost.to_varinfo Multithreaded in - if ctx.ask (GhostVarAvailable var) then + if ctx.ask (GhostVarAvailable Multithreaded) then ( + let var = WitnessGhost.to_varinfo Multithreaded in Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) else Invariant.none ) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 129d0d5d69..7a667c9c43 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -342,9 +342,10 @@ struct acc ) cpa Invariant.none in - let var = WitnessGhost.to_varinfo (Locked m') in - if ask.f (GhostVarAvailable var) then + if ask.f (GhostVarAvailable (Locked m')) then ( + let var = WitnessGhost.to_varinfo (Locked m') in Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) else Invariant.none | g -> (* global *) @@ -861,9 +862,10 @@ struct else ( let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) Q.AD.fold (fun m acc -> - let var = WitnessGhost.to_varinfo (Locked m) in - if ask.f (GhostVarAvailable var) then + if ask.f (GhostVarAvailable (Locked m)) then ( + let var = WitnessGhost.to_varinfo (Locked m) in Invariant.(of_exp (Lval (GoblintCil.var var)) || acc) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) else Invariant.none ) locks inv diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 21a12db7a1..5ffdac6110 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -82,8 +82,7 @@ struct let query ctx (type a) (q: a Queries.t): a Queries.result = match q with - | GhostVarAvailable vi -> - GobOption.exists (ghost_var_available ctx) (WitnessGhost.from_varinfo vi) + | GhostVarAvailable v -> ghost_var_available ctx v | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in begin match g with diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 9a50af1907..44a0402a93 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -130,7 +130,7 @@ type _ t = | TmpSpecial: Mval.Exp.t -> ML.t t | MaySignedOverflow: exp -> MayBool.t t | YamlEntryGlobal: Obj.t * YamlWitnessType.Task.t -> YS.t t - | GhostVarAvailable: varinfo -> MustBool.t t + | GhostVarAvailable: WitnessGhostVar.t -> MayBool.t t type 'a result = 'a @@ -203,7 +203,7 @@ struct | TmpSpecial _ -> (module ML) | MaySignedOverflow _ -> (module MayBool) | YamlEntryGlobal _ -> (module YS) - | GhostVarAvailable _ -> (module MustBool) + | GhostVarAvailable _ -> (module MayBool) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -275,7 +275,7 @@ struct | TmpSpecial _ -> ML.top () | MaySignedOverflow _ -> MayBool.top () | YamlEntryGlobal _ -> YS.top () - | GhostVarAvailable _ -> MustBool.top () + | GhostVarAvailable _ -> MayBool.top () end (* The type any_query can't be directly defined in Any as t, @@ -401,7 +401,7 @@ struct | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 | Any (MaySignedOverflow e1), Any (MaySignedOverflow e2) -> CilType.Exp.compare e1 e2 - | Any (GhostVarAvailable vi1), Any (GhostVarAvailable vi2) -> CilType.Varinfo.compare vi1 vi2 + | Any (GhostVarAvailable v1), Any (GhostVarAvailable v2) -> WitnessGhostVar.compare v1 v2 (* only argumentless queries should remain *) | _, _ -> Stdlib.compare (order a) (order b) @@ -446,7 +446,7 @@ struct | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv | Any (MaySignedOverflow e) -> CilType.Exp.hash e - | Any (GhostVarAvailable vi) -> CilType.Varinfo.hash vi + | Any (GhostVarAvailable v) -> WitnessGhostVar.hash v (* IterSysVars: *) (* - argument is a function and functions cannot be compared in any meaningful way. *) (* - doesn't matter because IterSysVars is always queried from outside of the analysis, so MCP's query caching is not done for it. *) @@ -512,7 +512,7 @@ struct | Any IsEverMultiThreaded -> Pretty.dprintf "IsEverMultiThreaded" | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv | Any (MaySignedOverflow e) -> Pretty.dprintf "MaySignedOverflow %a" CilType.Exp.pretty e - | Any (GhostVarAvailable vi) -> Pretty.dprintf "GhostVarAvailable %a" CilType.Varinfo.pretty vi + | Any (GhostVarAvailable v) -> Pretty.dprintf "GhostVarAvailable %a" WitnessGhostVar.pretty v end let to_value_domain_ask (ask: ask) = From d8bd13d7a6770e75fc14958a69510b964bdc4937 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 19 Apr 2024 16:23:05 +0300 Subject: [PATCH 072/566] Exclude WitnessGhostVar from docs check --- scripts/goblint-lib-modules.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 017530f838..98b8acd39f 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -44,6 +44,7 @@ "MessageCategory", # included in Messages "PreValueDomain", # included in ValueDomain + "WitnessGhostVar", # included in WitnessGhost "ConfigVersion", "ConfigProfile", From 947d6bc4382c36778c5f6de2d07b07be9637a7ab Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 12:51:14 +0300 Subject: [PATCH 073/566] Fix __VERIFIER_atomic special mutex ghost varialbe name --- src/witness/witnessGhostVar.ml | 5 +- tests/regression/29-svcomp/16-atomic_priv.t | 88 +++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/regression/29-svcomp/16-atomic_priv.t diff --git a/src/witness/witnessGhostVar.ml b/src/witness/witnessGhostVar.ml index afcf5d4dba..bc0f98f915 100644 --- a/src/witness/witnessGhostVar.ml +++ b/src/witness/witnessGhostVar.ml @@ -8,10 +8,13 @@ type t = let name_varinfo = function | Locked (Addr (v, _) as l) -> let name = + if CilType.Varinfo.equal v LibraryFunctions.verifier_atomic_var then + "__VERIFIER_atomic" + else if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo v then Printf.sprintf "alloc_%s%d" (if v.vid < 0 then "m" else "") (abs v.vid) (* turn minus into valid C name *) else - LockDomain.Addr.show l (* TODO: valid names with interval offsets, etc *) + LockDomain.Addr.show l (* TODO: valid names with fields, interval offsets, etc *) in name ^ "_locked" | Locked _ -> assert false diff --git a/tests/regression/29-svcomp/16-atomic_priv.t b/tests/regression/29-svcomp/16-atomic_priv.t new file mode 100644 index 0000000000..98584b96d0 --- /dev/null +++ b/tests/regression/29-svcomp/16-atomic_priv.t @@ -0,0 +1,88 @@ + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection-atomic --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 16-atomic_priv.c + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:12:3-12:33) + [Success][Assert] Assertion "myglobal == 6" will succeed (16-atomic_priv.c:14:3-14:33) + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:16:3-16:33) + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:24:3-24:33) + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:26:3-26:33) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 17 + dead: 0 + total lines: 17 + [Warning][Race] Memory location myglobal (race with conf. 110): (16-atomic_priv.c:8:5-8:17) + write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:13:3-13:13) + write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:15:3-15:13) + read with [mhp:{created={[main, t_fun@16-atomic_priv.c:23:3-23:40]}}, thread:[main]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:24:3-24:33) + [Info][Witness] witness generation summary: + total generation entries: 9 + [Info][Race] Memory locations race summary: + safe: 0 + vulnerable: 0 + unsafe: 1 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + - entry_type: ghost_update + variable: __VERIFIER_atomic_locked + expression: "1" + location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 25 + column: 3 + function: main + - entry_type: ghost_update + variable: __VERIFIER_atomic_locked + expression: "1" + location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: __VERIFIER_atomic_locked + expression: "0" + location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 27 + column: 3 + function: main + - entry_type: ghost_update + variable: __VERIFIER_atomic_locked + expression: "0" + location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: __VERIFIER_atomic_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || myglobal == 5' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (__VERIFIER_atomic_locked || myglobal == 5)' + type: assertion + format: C From 4ada6eb2ced86b2021bde12937a237d716b6daa4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 12:55:56 +0300 Subject: [PATCH 074/566] Avoid emitting useless protected invariants from protection privatization --- src/analyses/basePriv.ml | 2 ++ tests/regression/29-svcomp/16-atomic_priv.t | 7 +------ tests/regression/56-witness/64-ghost-multiple-protecting.t | 7 +------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 7a667c9c43..7cadf637ad 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -859,6 +859,8 @@ struct let locks = ask.f (Q.MustProtectingLocks g') in if Q.AD.is_top locks || Q.AD.is_empty locks then Invariant.none + else if VD.equal (getg (V.protected g')) (getg (V.unprotected g')) then + Invariant.none (* don't output protected invariant because it's the same as unprotected *) else ( let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) Q.AD.fold (fun m acc -> diff --git a/tests/regression/29-svcomp/16-atomic_priv.t b/tests/regression/29-svcomp/16-atomic_priv.t index 98584b96d0..15425f68dd 100644 --- a/tests/regression/29-svcomp/16-atomic_priv.t +++ b/tests/regression/29-svcomp/16-atomic_priv.t @@ -13,7 +13,7 @@ write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:15:3-15:13) read with [mhp:{created={[main, t_fun@16-atomic_priv.c:23:3-23:40]}}, thread:[main]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:24:3-24:33) [Info][Witness] witness generation summary: - total generation entries: 9 + total generation entries: 8 [Info][Race] Memory locations race summary: safe: 0 vulnerable: 0 @@ -81,8 +81,3 @@ string: '! multithreaded || myglobal == 5' type: assertion format: C - - entry_type: flow_insensitive_invariant - flow_insensitive_invariant: - string: '! multithreaded || (__VERIFIER_atomic_locked || myglobal == 5)' - type: assertion - format: C diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.t b/tests/regression/56-witness/64-ghost-multiple-protecting.t index d51db2285e..53323355c5 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.t +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.t @@ -4,7 +4,7 @@ dead: 0 total lines: 19 [Info][Witness] witness generation summary: - total generation entries: 18 + total generation entries: 17 [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 @@ -133,11 +133,6 @@ protection doesn't have precise protected invariant for g2. string: '! multithreaded || (m2_locked || (m1_locked || g1 == 0))' type: assertion format: C - - entry_type: flow_insensitive_invariant - flow_insensitive_invariant: - string: '! multithreaded || (m2_locked || (m1_locked || (0 <= g2 && g2 <= 1)))' - type: assertion - format: C - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (0 <= g2 && g2 <= 1)' From a7d43a938181ca404666147d2fc705c09a0fa8e4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 13:04:26 +0300 Subject: [PATCH 075/566] Avoid useless work in mutex-meet invariant_global if ghost variable isn't available --- src/analyses/basePriv.ml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 7cadf637ad..799290c4fe 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -333,16 +333,16 @@ struct let invariant_global (ask: Q.ask) getg = function | `Left m' as m -> (* mutex *) - let cpa = getg m in - let inv = CPA.fold (fun v _ acc -> - if ask.f (MustBeProtectedBy {mutex = m'; global = v; write = true; protection = Strong}) then - let inv = ValueDomain.invariant_global (fun g -> CPA.find g cpa) v in - Invariant.(acc && inv) - else - acc - ) cpa Invariant.none - in if ask.f (GhostVarAvailable (Locked m')) then ( + let cpa = getg m in + let inv = CPA.fold (fun v _ acc -> + if ask.f (MustBeProtectedBy {mutex = m'; global = v; write = true; protection = Strong}) then + let inv = ValueDomain.invariant_global (fun g -> CPA.find g cpa) v in + Invariant.(acc && inv) + else + acc + ) cpa Invariant.none + in let var = WitnessGhost.to_varinfo (Locked m') in Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) From d6abc0b4f14dccbd1c500ce3a9e23f72497aa966 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 13:44:29 +0300 Subject: [PATCH 076/566] Fix struct field mutex ghost variable name --- src/witness/witnessGhostVar.ml | 11 ++- tests/regression/13-privatized/25-struct_nr.t | 83 +++++++++++++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 tests/regression/13-privatized/25-struct_nr.t diff --git a/src/witness/witnessGhostVar.ml b/src/witness/witnessGhostVar.ml index bc0f98f915..cac48050de 100644 --- a/src/witness/witnessGhostVar.ml +++ b/src/witness/witnessGhostVar.ml @@ -6,7 +6,7 @@ type t = [@@deriving eq, ord, hash] let name_varinfo = function - | Locked (Addr (v, _) as l) -> + | Locked (Addr (v, os)) -> let name = if CilType.Varinfo.equal v LibraryFunctions.verifier_atomic_var then "__VERIFIER_atomic" @@ -14,9 +14,14 @@ let name_varinfo = function if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo v then Printf.sprintf "alloc_%s%d" (if v.vid < 0 then "m" else "") (abs v.vid) (* turn minus into valid C name *) else - LockDomain.Addr.show l (* TODO: valid names with fields, interval offsets, etc *) + Basetype.Variables.show v in - name ^ "_locked" + let rec offs: LockDomain.Addr.Offs.t -> string = function + | `NoOffset -> "" + | `Field (f, os') -> "_" ^ f.fname ^ offs os' + | `Index (i, os') -> failwith "TODO" (* TODO: valid names with interval offsets, etc *) + in + name ^ offs os ^ "_locked" | Locked _ -> assert false | Multithreaded -> "multithreaded" diff --git a/tests/regression/13-privatized/25-struct_nr.t b/tests/regression/13-privatized/25-struct_nr.t new file mode 100644 index 0000000000..f3ebcd1c52 --- /dev/null +++ b/tests/regression/13-privatized/25-struct_nr.t @@ -0,0 +1,83 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 25-struct_nr.c + [Success][Assert] Assertion "glob1 == 5" will succeed (25-struct_nr.c:26:3-26:30) + [Success][Assert] Assertion "t == 5" will succeed (25-struct_nr.c:16:3-16:26) + [Success][Assert] Assertion "glob1 == -10" will succeed (25-struct_nr.c:18:3-18:32) + [Success][Assert] Assertion "glob1 == 6" will succeed (25-struct_nr.c:30:3-30:30) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + total generation entries: 9 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 27 + column: 3 + function: main + - entry_type: ghost_update + variable: lock1_mutex_locked + expression: "1" + location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + - entry_type: ghost_update + variable: lock1_mutex_locked + expression: "1" + location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: lock1_mutex_locked + expression: "0" + location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 32 + column: 3 + function: main + - entry_type: ghost_update + variable: lock1_mutex_locked + expression: "0" + location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: lock1_mutex_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (lock1_mutex_locked || glob1 == 5)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || ((-128 <= glob1 && glob1 <= 127) && glob1 != 0)' + type: assertion + format: C From 6db1d04eeb551a5726bb373e7cc6e6e0394a4368 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 14:34:05 +0300 Subject: [PATCH 077/566] Fix definite array index mutex ghost variable name --- src/witness/witnessGhostVar.ml | 5 +- tests/regression/13-privatized/80-idx_priv.c | 26 +++++++ tests/regression/13-privatized/80-idx_priv.t | 80 ++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 tests/regression/13-privatized/80-idx_priv.c create mode 100644 tests/regression/13-privatized/80-idx_priv.t diff --git a/src/witness/witnessGhostVar.ml b/src/witness/witnessGhostVar.ml index cac48050de..cec61b0e2d 100644 --- a/src/witness/witnessGhostVar.ml +++ b/src/witness/witnessGhostVar.ml @@ -19,7 +19,10 @@ let name_varinfo = function let rec offs: LockDomain.Addr.Offs.t -> string = function | `NoOffset -> "" | `Field (f, os') -> "_" ^ f.fname ^ offs os' - | `Index (i, os') -> failwith "TODO" (* TODO: valid names with interval offsets, etc *) + | `Index (i, os') -> + match ValueDomain.ID.to_int i with + | Some i -> assert Z.Compare.(i >= Z.zero); "_" ^ Z.to_string i + | _ -> assert false (* must locksets cannot have ambiguous indices *) in name ^ offs os ^ "_locked" | Locked _ -> assert false diff --git a/tests/regression/13-privatized/80-idx_priv.c b/tests/regression/13-privatized/80-idx_priv.c new file mode 100644 index 0000000000..ed0e8d3228 --- /dev/null +++ b/tests/regression/13-privatized/80-idx_priv.c @@ -0,0 +1,26 @@ +#include +#include + +int data; +pthread_mutex_t m[10]; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[4]); + data++; // NORACE + data--; // NORACE + pthread_mutex_unlock(&m[4]); + return NULL; +} + +int main() { + for (int i = 0; i < 10; i++) + pthread_mutex_init(&m[i], NULL); + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&m[4]); + __goblint_check(data == 0); // NORACE + pthread_mutex_unlock(&m[4]); + return 0; +} + diff --git a/tests/regression/13-privatized/80-idx_priv.t b/tests/regression/13-privatized/80-idx_priv.t new file mode 100644 index 0000000000..698744924c --- /dev/null +++ b/tests/regression/13-privatized/80-idx_priv.t @@ -0,0 +1,80 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 80-idx_priv.c + [Success][Assert] Assertion "data == 0" will succeed (80-idx_priv.c:22:3-22:29) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Witness] witness generation summary: + total generation entries: 9 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 80-idx_priv.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: main + - entry_type: ghost_update + variable: m_4_locked + expression: "1" + location: + file_name: 80-idx_priv.c + file_hash: $FILE_HASH + line: 21 + column: 3 + function: main + - entry_type: ghost_update + variable: m_4_locked + expression: "1" + location: + file_name: 80-idx_priv.c + file_hash: $FILE_HASH + line: 8 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: m_4_locked + expression: "0" + location: + file_name: 80-idx_priv.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + - entry_type: ghost_update + variable: m_4_locked + expression: "0" + location: + file_name: 80-idx_priv.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m_4_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m_4_locked || data == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= data && data <= 1)' + type: assertion + format: C From 53a714fe979757f8f71c1ea408f3a3e5ccd4120c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 15:28:26 +0300 Subject: [PATCH 078/566] Make non-definite ghost variables unavailable --- src/analyses/mutexGhosts.ml | 2 +- .../56-witness/68-ghost-ambiguous-idx.c | 28 +++++++ .../56-witness/68-ghost-ambiguous-idx.t | 78 +++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 tests/regression/56-witness/68-ghost-ambiguous-idx.c create mode 100644 tests/regression/56-witness/68-ghost-ambiguous-idx.t diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 5ffdac6110..128355e919 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -77,7 +77,7 @@ struct ctx.local let ghost_var_available ctx = function - | WitnessGhost.Var.Locked lock -> not (G.lock (ctx.global (V.lock lock)): bool) + | WitnessGhost.Var.Locked lock -> LockDomain.Addr.is_definite lock && not (G.lock (ctx.global (V.lock lock))) | Multithreaded -> true let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/tests/regression/56-witness/68-ghost-ambiguous-idx.c b/tests/regression/56-witness/68-ghost-ambiguous-idx.c new file mode 100644 index 0000000000..7babbe003c --- /dev/null +++ b/tests/regression/56-witness/68-ghost-ambiguous-idx.c @@ -0,0 +1,28 @@ +#include +#include + +int data; +pthread_mutex_t m[10]; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[4]); + data++; + data--; + pthread_mutex_unlock(&m[4]); + return NULL; +} + +int main() { + for (int i = 0; i < 10; i++) + pthread_mutex_init(&m[i], NULL); + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + int r; + int j = r ? 4 : 5; + pthread_mutex_lock(&m[r]); + __goblint_check(data == 0); // UNKNOWN! + pthread_mutex_unlock(&m[4]); + return 0; +} + diff --git a/tests/regression/56-witness/68-ghost-ambiguous-idx.t b/tests/regression/56-witness/68-ghost-ambiguous-idx.t new file mode 100644 index 0000000000..0f6191188e --- /dev/null +++ b/tests/regression/56-witness/68-ghost-ambiguous-idx.t @@ -0,0 +1,78 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 68-ghost-ambiguous-idx.c + [Warning][Assert] Assertion "data == 0" is unknown. (68-ghost-ambiguous-idx.c:24:3-24:29) + [Warning][Unknown] unlocking mutex (m[4]) which may not be held (68-ghost-ambiguous-idx.c:25:3-25:30) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 15 + dead: 0 + total lines: 15 + [Warning][Race] Memory location data (race with conf. 110): (68-ghost-ambiguous-idx.c:4:5-4:9) + write with [lock:{m[4]}, thread:[main, t_fun@68-ghost-ambiguous-idx.c:20:3-20:40]] (conf. 110) (exp: & data) (68-ghost-ambiguous-idx.c:9:3-9:9) + write with [lock:{m[4]}, thread:[main, t_fun@68-ghost-ambiguous-idx.c:20:3-20:40]] (conf. 110) (exp: & data) (68-ghost-ambiguous-idx.c:10:3-10:9) + read with [mhp:{created={[main, t_fun@68-ghost-ambiguous-idx.c:20:3-20:40]}}, thread:[main]] (conf. 110) (exp: & data) (68-ghost-ambiguous-idx.c:24:3-24:29) + [Info][Witness] witness generation summary: + total generation entries: 8 + [Info][Race] Memory locations race summary: + safe: 0 + vulnerable: 0 + unsafe: 1 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 68-ghost-ambiguous-idx.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: main + - entry_type: ghost_update + variable: m_4_locked + expression: "1" + location: + file_name: 68-ghost-ambiguous-idx.c + file_hash: $FILE_HASH + line: 8 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: m_4_locked + expression: "0" + location: + file_name: 68-ghost-ambiguous-idx.c + file_hash: $FILE_HASH + line: 25 + column: 3 + function: main + - entry_type: ghost_update + variable: m_4_locked + expression: "0" + location: + file_name: 68-ghost-ambiguous-idx.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m_4_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m_4_locked || data == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= data && data <= 1)' + type: assertion + format: C + +TODO: there shouldn't be invariant with m_4_locked because it's ambiguously used From 413b2e17dd3e4e8b7e53ec50af475eee539d44ee Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Apr 2024 15:32:59 +0300 Subject: [PATCH 079/566] Disable mutex ghosts with indices --- src/analyses/mutexGhosts.ml | 3 +- tests/regression/13-privatized/80-idx_priv.t | 50 ++----------------- .../56-witness/68-ghost-ambiguous-idx.t | 41 +-------------- 3 files changed, 6 insertions(+), 88 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 128355e919..b7001c6c2f 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -77,7 +77,8 @@ struct ctx.local let ghost_var_available ctx = function - | WitnessGhost.Var.Locked lock -> LockDomain.Addr.is_definite lock && not (G.lock (ctx.global (V.lock lock))) + | WitnessGhost.Var.Locked (Addr (v, o) as lock) -> not (LockDomain.Offs.contains_index o) && not (G.lock (ctx.global (V.lock lock))) + | Locked _ -> false | Multithreaded -> true let query ctx (type a) (q: a Queries.t): a Queries.result = diff --git a/tests/regression/13-privatized/80-idx_priv.t b/tests/regression/13-privatized/80-idx_priv.t index 698744924c..bf15cfb538 100644 --- a/tests/regression/13-privatized/80-idx_priv.t +++ b/tests/regression/13-privatized/80-idx_priv.t @@ -5,7 +5,7 @@ dead: 0 total lines: 14 [Info][Witness] witness generation summary: - total generation entries: 9 + total generation entries: 3 [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 @@ -22,59 +22,15 @@ line: 20 column: 3 function: main - - entry_type: ghost_update - variable: m_4_locked - expression: "1" - location: - file_name: 80-idx_priv.c - file_hash: $FILE_HASH - line: 21 - column: 3 - function: main - - entry_type: ghost_update - variable: m_4_locked - expression: "1" - location: - file_name: 80-idx_priv.c - file_hash: $FILE_HASH - line: 8 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m_4_locked - expression: "0" - location: - file_name: 80-idx_priv.c - file_hash: $FILE_HASH - line: 23 - column: 3 - function: main - - entry_type: ghost_update - variable: m_4_locked - expression: "0" - location: - file_name: 80-idx_priv.c - file_hash: $FILE_HASH - line: 11 - column: 3 - function: t_fun - entry_type: ghost_variable variable: multithreaded scope: global type: int initial: "0" - - entry_type: ghost_variable - variable: m_4_locked - scope: global - type: int - initial: "0" - - entry_type: flow_insensitive_invariant - flow_insensitive_invariant: - string: '! multithreaded || (m_4_locked || data == 0)' - type: assertion - format: C - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (0 <= data && data <= 1)' type: assertion format: C + +TODO: protected invariant with m_4_locked without making 56-witness/68-ghost-ambiguous-idx unsound diff --git a/tests/regression/56-witness/68-ghost-ambiguous-idx.t b/tests/regression/56-witness/68-ghost-ambiguous-idx.t index 0f6191188e..48837fcabb 100644 --- a/tests/regression/56-witness/68-ghost-ambiguous-idx.t +++ b/tests/regression/56-witness/68-ghost-ambiguous-idx.t @@ -10,7 +10,7 @@ write with [lock:{m[4]}, thread:[main, t_fun@68-ghost-ambiguous-idx.c:20:3-20:40]] (conf. 110) (exp: & data) (68-ghost-ambiguous-idx.c:10:3-10:9) read with [mhp:{created={[main, t_fun@68-ghost-ambiguous-idx.c:20:3-20:40]}}, thread:[main]] (conf. 110) (exp: & data) (68-ghost-ambiguous-idx.c:24:3-24:29) [Info][Witness] witness generation summary: - total generation entries: 8 + total generation entries: 3 [Info][Race] Memory locations race summary: safe: 0 vulnerable: 0 @@ -27,52 +27,13 @@ line: 20 column: 3 function: main - - entry_type: ghost_update - variable: m_4_locked - expression: "1" - location: - file_name: 68-ghost-ambiguous-idx.c - file_hash: $FILE_HASH - line: 8 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m_4_locked - expression: "0" - location: - file_name: 68-ghost-ambiguous-idx.c - file_hash: $FILE_HASH - line: 25 - column: 3 - function: main - - entry_type: ghost_update - variable: m_4_locked - expression: "0" - location: - file_name: 68-ghost-ambiguous-idx.c - file_hash: $FILE_HASH - line: 11 - column: 3 - function: t_fun - entry_type: ghost_variable variable: multithreaded scope: global type: int initial: "0" - - entry_type: ghost_variable - variable: m_4_locked - scope: global - type: int - initial: "0" - - entry_type: flow_insensitive_invariant - flow_insensitive_invariant: - string: '! multithreaded || (m_4_locked || data == 0)' - type: assertion - format: C - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (0 <= data && data <= 1)' type: assertion format: C - -TODO: there shouldn't be invariant with m_4_locked because it's ambiguously used From ea849fbb840452090517c6395c78fbffd5226ea4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 10:28:28 +0300 Subject: [PATCH 080/566] Detect thread create nodes in mutexGhosts --- src/analyses/mutexGhosts.ml | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index b7001c6c2f..803f80f3e6 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -33,11 +33,16 @@ struct include BoolDomain.MayBool let name () = "multithread" end + module ThreadCreate = + struct + include BoolDomain.MayBool + let name () = "threadcreate" + end module G = struct - include Lattice.Lift2 (Lattice.Prod3 (Locked) (Unlocked) (MultiThread)) (BoolDomain.MayBool) + include Lattice.Lift2 (Lattice.Prod4 (Locked) (Unlocked) (MultiThread) (ThreadCreate)) (BoolDomain.MayBool) let node = function - | `Bot -> (Locked.bot (), Unlocked.bot (), MultiThread.bot ()) + | `Bot -> (Locked.bot (), Unlocked.bot (), MultiThread.bot (), ThreadCreate.bot ()) | `Lifted1 x -> x | _ -> failwith "MutexGhosts.node" let lock = function @@ -51,9 +56,9 @@ struct let event ctx e octx = begin match e with | Events.Lock (l, _) -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ())); + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot (), ThreadCreate.bot ())); if !AnalysisState.postsolving then ( - let (locked, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in + let (locked, _, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in if Locked.cardinal locked > 1 then ( Locked.iter (fun lock -> ctx.sideg (V.lock lock) (G.create_lock true) @@ -61,9 +66,9 @@ struct ); ) | Events.Unlock l -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ())); + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot (), ThreadCreate.bot ())); if !AnalysisState.postsolving then ( - let (_, unlocked, _) = G.node (ctx.global (V.node ctx.prev_node)) in + let (_, unlocked, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in if Locked.cardinal unlocked > 1 then ( Locked.iter (fun lock -> ctx.sideg (V.lock lock) (G.create_lock true) @@ -71,11 +76,15 @@ struct ); ) | Events.EnterMultiThreaded -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true)) + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true, ThreadCreate.bot ())) | _ -> () end; ctx.local + let threadspawn ctx ~multiple lval f args octx = + ctx.sideg (V.node ctx.node) (G.create_node (Locked.bot (), Unlocked.bot (), MultiThread.bot (), true)); + ctx.local + let ghost_var_available ctx = function | WitnessGhost.Var.Locked (Addr (v, o) as lock) -> not (LockDomain.Offs.contains_index o) && not (G.lock (ctx.global (V.lock lock))) | Locked _ -> false @@ -88,7 +97,7 @@ struct let g: V.t = Obj.obj g in begin match g with | `Left g' -> - let (locked, unlocked, multithread) = G.node (ctx.global g) in + let (locked, unlocked, multithread, threadcreate) = G.node (ctx.global g) in let g = g' in let entries = (* TODO: do variable_entry-s only once *) From 594beaceed70cb0e85ed0b56c9c26711becc1956 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 10:44:00 +0300 Subject: [PATCH 081/566] Refactor mutexGhosts thread creation collection --- src/analyses/mutexGhosts.ml | 44 +++++++++++++++++++++---------------- src/domains/queries.ml | 1 + 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 803f80f3e6..9ddde5d37e 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -2,20 +2,25 @@ open Analyses +module NodeSet = Queries.NS + module Spec = struct include UnitAnalysis.Spec let name () = "mutexGhosts" + module ThreadCreate = Printable.UnitConf (struct let name = "threadcreate" end) module V = struct - include Printable.Either (Node) (LockDomain.Addr) + include Printable.Either3 (Node) (LockDomain.Addr) (ThreadCreate) let node x = `Left x - let lock x = `Right x + let lock x = `Middle x + let threadcreate = `Right () let is_write_only = function | `Left _ -> false - | `Right _ -> true + | `Middle _ -> true + | `Right _ -> false end module Locked = @@ -33,32 +38,32 @@ struct include BoolDomain.MayBool let name () = "multithread" end - module ThreadCreate = - struct - include BoolDomain.MayBool - let name () = "threadcreate" - end module G = struct - include Lattice.Lift2 (Lattice.Prod4 (Locked) (Unlocked) (MultiThread) (ThreadCreate)) (BoolDomain.MayBool) + include Lattice.Lift2 (Lattice.Prod3 (Locked) (Unlocked) (MultiThread)) (Lattice.Lift2 (BoolDomain.MayBool) (NodeSet)) let node = function - | `Bot -> (Locked.bot (), Unlocked.bot (), MultiThread.bot (), ThreadCreate.bot ()) + | `Bot -> (Locked.bot (), Unlocked.bot (), MultiThread.bot ()) | `Lifted1 x -> x | _ -> failwith "MutexGhosts.node" let lock = function | `Bot -> BoolDomain.MayBool.bot () - | `Lifted2 x -> x + | `Lifted2 (`Lifted1 x) -> x | _ -> failwith "MutexGhosts.lock" + let threadcreate = function + | `Bot -> NodeSet.bot () + | `Lifted2 (`Lifted2 x) -> x + | _ -> failwith "MutexGhosts.threadcreate" let create_node node = `Lifted1 node - let create_lock lock = `Lifted2 lock + let create_lock lock = `Lifted2 (`Lifted1 lock) + let create_threadcreate threadcreate = `Lifted2 (`Lifted2 threadcreate) end let event ctx e octx = begin match e with | Events.Lock (l, _) -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot (), ThreadCreate.bot ())); + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ())); if !AnalysisState.postsolving then ( - let (locked, _, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in + let (locked, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in if Locked.cardinal locked > 1 then ( Locked.iter (fun lock -> ctx.sideg (V.lock lock) (G.create_lock true) @@ -66,9 +71,9 @@ struct ); ) | Events.Unlock l -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot (), ThreadCreate.bot ())); + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ())); if !AnalysisState.postsolving then ( - let (_, unlocked, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in + let (_, unlocked, _) = G.node (ctx.global (V.node ctx.prev_node)) in if Locked.cardinal unlocked > 1 then ( Locked.iter (fun lock -> ctx.sideg (V.lock lock) (G.create_lock true) @@ -76,13 +81,13 @@ struct ); ) | Events.EnterMultiThreaded -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true, ThreadCreate.bot ())) + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true)) | _ -> () end; ctx.local let threadspawn ctx ~multiple lval f args octx = - ctx.sideg (V.node ctx.node) (G.create_node (Locked.bot (), Unlocked.bot (), MultiThread.bot (), true)); + ctx.sideg V.threadcreate (G.create_threadcreate (NodeSet.singleton ctx.node)); ctx.local let ghost_var_available ctx = function @@ -97,7 +102,7 @@ struct let g: V.t = Obj.obj g in begin match g with | `Left g' -> - let (locked, unlocked, multithread, threadcreate) = G.node (ctx.global g) in + let (locked, unlocked, multithread) = G.node (ctx.global g) in let g = g' in let entries = (* TODO: do variable_entry-s only once *) @@ -144,6 +149,7 @@ struct entries in entries + | `Middle _ -> Queries.Result.top q | `Right _ -> Queries.Result.top q end | _ -> Queries.Result.top q diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 44a0402a93..515198854d 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -10,6 +10,7 @@ module LS = VDQ.LS module TS = SetDomain.ToppedSet (CilType.Typ) (struct let topname = "All" end) module ES = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) module VS = SetDomain.ToppedSet (CilType.Varinfo) (struct let topname = "All" end) +module NS = SetDomain.ToppedSet (Node) (struct let topname = "All" end) module NFL = WrapperFunctionAnalysis0.NodeFlatLattice module TC = WrapperFunctionAnalysis0.ThreadCreateUniqueCount From 584b78842a0a38fee98c73d59629a707218b0e0a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Apr 2024 11:06:39 +0300 Subject: [PATCH 082/566] Add option to emit flow_insensitive_invariant-s as location_invariant-s --- src/analyses/mutexGhosts.ml | 1 + src/config/options.schema.json | 6 +++ src/domains/queries.ml | 5 +++ src/witness/yamlWitness.ml | 21 ++++++++-- tests/regression/13-privatized/74-mutex.t | 50 ++++++++++++++++++++++- 5 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 9ddde5d37e..75195e4662 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -152,6 +152,7 @@ struct | `Middle _ -> Queries.Result.top q | `Right _ -> Queries.Result.top q end + | InvariantGlobalNodes -> (G.threadcreate (ctx.global V.threadcreate): NodeSet.t) | _ -> Queries.Result.top q end diff --git a/src/config/options.schema.json b/src/config/options.schema.json index db93e74ff4..3065325f4e 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -2493,6 +2493,12 @@ "description": "Emit invariants with typedef-ed types (e.g. in casts). Our validator cannot parse these.", "type": "boolean", "default": true + }, + "flow_insensitive-as-location": { + "title": "witness.invariant.flow_insensitive-as-location", + "description": "Emit flow-insensitive invariants as location invariants at certain locations.", + "type": "boolean", + "default": false } }, "additionalProperties": false diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 515198854d..152fb5f1a5 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -132,6 +132,7 @@ type _ t = | MaySignedOverflow: exp -> MayBool.t t | YamlEntryGlobal: Obj.t * YamlWitnessType.Task.t -> YS.t t | GhostVarAvailable: WitnessGhostVar.t -> MayBool.t t + | InvariantGlobalNodes: NS.t t (* TODO: V.t argument? *) type 'a result = 'a @@ -205,6 +206,7 @@ struct | MaySignedOverflow _ -> (module MayBool) | YamlEntryGlobal _ -> (module YS) | GhostVarAvailable _ -> (module MayBool) + | InvariantGlobalNodes -> (module NS) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -277,6 +279,7 @@ struct | MaySignedOverflow _ -> MayBool.top () | YamlEntryGlobal _ -> YS.top () | GhostVarAvailable _ -> MayBool.top () + | InvariantGlobalNodes -> NS.top () end (* The type any_query can't be directly defined in Any as t, @@ -346,6 +349,7 @@ struct | Any (YamlEntryGlobal _) -> 59 | Any (MustProtectingLocks _) -> 60 | Any (GhostVarAvailable _) -> 61 + | Any InvariantGlobalNodes -> 62 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -514,6 +518,7 @@ struct | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv | Any (MaySignedOverflow e) -> Pretty.dprintf "MaySignedOverflow %a" CilType.Exp.pretty e | Any (GhostVarAvailable v) -> Pretty.dprintf "GhostVarAvailable %a" WitnessGhostVar.pretty v + | Any InvariantGlobalNodes -> Pretty.dprintf "InvariantGlobalNodes" end let to_value_domain_ask (ask: ask) = diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 9eafae009f..49fe889c22 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -325,18 +325,33 @@ struct (* Generate flow-insensitive invariants *) let entries = if entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type then ( + let ns = R.ask_global InvariantGlobalNodes in GHT.fold (fun g v acc -> match g with | `Left g -> (* Spec global *) - begin match R.ask_global (InvariantGlobal (Obj.repr g)) with - | `Lifted inv -> + begin match R.ask_global (InvariantGlobal (Obj.repr g)), GobConfig.get_bool "witness.invariant.flow_insensitive-as-location" with + | `Lifted inv, false -> 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 *) + | `Lifted inv, true -> + (* TODO: or do at location_invariant loop for each node and query if should also do global invariants there? *) + let invs = WitnessUtil.InvariantExp.process_exp inv in + Queries.NS.fold (fun n acc -> + let fundec = Node.find_fundec n in + let loc = Node.location n in + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in + List.fold_left (fun acc inv -> + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.location_invariant ~task ~location ~invariant in + entry :: acc + ) acc invs + ) ns acc + | `Bot, _ | `Top, _ -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) acc end | `Right _ -> (* contexts global *) diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index 6f84aa184f..a00f49eb1a 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -15,7 +15,7 @@ unsafe: 0 total memory locations: 1 - $ yamlWitnessStrip < witness.yml + $ yamlWitnessStrip < witness.yml | tee witness.flow_insensitive.yml - entry_type: ghost_update variable: multithreaded expression: "1" @@ -82,6 +82,54 @@ type: assertion format: C +Flow-insensitive invariants as location invariants. + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' --enable witness.invariant.flow_insensitive-as-location 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 15 + dead: 1 + total lines: 16 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Witness] witness generation summary: + total generation entries: 9 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml > witness.location.yml + + $ diff witness.flow_insensitive.yml witness.location.yml + 56,57c56,63 + < - entry_type: flow_insensitive_invariant + < flow_insensitive_invariant: + --- + > - entry_type: location_invariant + > location: + > file_name: 74-mutex.c + > file_hash: $FILE_HASH + > line: 36 + > column: 3 + > function: main + > location_invariant: + 61,62c67,74 + < - entry_type: flow_insensitive_invariant + < flow_insensitive_invariant: + --- + > - entry_type: location_invariant + > location: + > file_name: 74-mutex.c + > file_hash: $FILE_HASH + > line: 36 + > column: 3 + > function: main + > location_invariant: + [1] + Should also work with earlyglobs. Earlyglobs shouldn't cause protected writes in multithreaded mode from being immediately published to protected invariant. From 8d5cc1275a71283c88cdd3136715da524cec7b51 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 25 Apr 2024 10:13:39 +0300 Subject: [PATCH 083/566] Add svcomp-ghost conf --- conf/svcomp-ghost.json | 145 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 conf/svcomp-ghost.json diff --git a/conf/svcomp-ghost.json b/conf/svcomp-ghost.json new file mode 100644 index 0000000000..62feb25993 --- /dev/null +++ b/conf/svcomp-ghost.json @@ -0,0 +1,145 @@ +{ + "ana": { + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "float": { + "interval": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "race", + "escape", + "expRelation", + "mhp", + "assert", + "var_eq", + "symb_locks", + "region", + "thread", + "threadJoins", + "mutexGhosts", + "pthreadMutexType" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "memLeak", + "threadflag" + ], + "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", + "ldv_kzalloc" + ] + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "octagon", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" + ] + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": false + }, + "yaml": { + "enabled": true, + "format-version": "2.0", + "entry-types": [ + "flow_insensitive_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": true, + "other": true, + "accessed": true, + "exact": true, + "all-locals": false, + "flow_insensitive-as-location": true, + "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]+" + ] + } + }, + "pre": { + "enabled": false + } +} From 96d862e4369df5355f80fcc69a94c7e53de806ac Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 25 Apr 2024 10:14:19 +0300 Subject: [PATCH 084/566] Use YAML witness format-version 0.1 for svcomp-ghost --- conf/svcomp-ghost.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/svcomp-ghost.json b/conf/svcomp-ghost.json index 62feb25993..229dd9ef46 100644 --- a/conf/svcomp-ghost.json +++ b/conf/svcomp-ghost.json @@ -114,7 +114,7 @@ }, "yaml": { "enabled": true, - "format-version": "2.0", + "format-version": "0.1", "entry-types": [ "flow_insensitive_invariant" ] From 257fa8cd374f4b339427eb4c2c52cf67d72aa298 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 25 Apr 2024 10:24:44 +0300 Subject: [PATCH 085/566] Test witness.invariant.flow_insensitive-as-location with for loop --- .../regression/13-privatized/04-priv_multi.t | 309 ++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 tests/regression/13-privatized/04-priv_multi.t diff --git a/tests/regression/13-privatized/04-priv_multi.t b/tests/regression/13-privatized/04-priv_multi.t new file mode 100644 index 0000000000..9bdf8ac5a7 --- /dev/null +++ b/tests/regression/13-privatized/04-priv_multi.t @@ -0,0 +1,309 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 04-priv_multi.c + [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) + [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) + [Warning][Deadcode] Function 'dispose' has dead code: + on line 53 (04-priv_multi.c:53-53) + on line 56 (04-priv_multi.c:56-56) + [Warning][Deadcode] Function 'process' has dead code: + on line 37 (04-priv_multi.c:37-37) + on line 40 (04-priv_multi.c:40-40) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 40 + dead: 4 + total lines: 44 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:25:10-25:11) + [Warning][Deadcode][CWE-571] condition 'A > 0' is always true (04-priv_multi.c:27:9-27:14) + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:45:10-45:11) + [Warning][Deadcode][CWE-571] condition 'B > 0' is always true (04-priv_multi.c:47:9-47:14) + [Info][Witness] witness generation summary: + total generation entries: 19 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + + $ yamlWitnessStrip < witness.yml | tee witness.flow_insensitive.yml + - entry_type: ghost_update + variable: mutex_B_locked + expression: "1" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 69 + column: 5 + function: main + - entry_type: ghost_update + variable: mutex_B_locked + expression: "1" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 46 + column: 5 + function: dispose + - entry_type: ghost_update + variable: mutex_B_locked + expression: "1" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 29 + column: 7 + function: process + - entry_type: ghost_update + variable: mutex_B_locked + expression: "0" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 73 + column: 5 + function: main + - entry_type: ghost_update + variable: mutex_B_locked + expression: "0" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 49 + column: 7 + function: dispose + - entry_type: ghost_update + variable: mutex_B_locked + expression: "0" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 32 + column: 7 + function: process + - entry_type: ghost_update + variable: mutex_A_locked + expression: "1" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 68 + column: 5 + function: main + - entry_type: ghost_update + variable: mutex_A_locked + expression: "1" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 26 + column: 5 + function: process + - entry_type: ghost_update + variable: mutex_A_locked + expression: "1" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 15 + column: 5 + function: generate + - entry_type: ghost_update + variable: mutex_A_locked + expression: "0" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 74 + column: 5 + function: main + - entry_type: ghost_update + variable: mutex_A_locked + expression: "0" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 34 + column: 7 + function: process + - entry_type: ghost_update + variable: mutex_A_locked + expression: "0" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 18 + column: 5 + function: generate + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 63 + column: 3 + function: main + - entry_type: ghost_variable + variable: mutex_B_locked + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: mutex_A_locked + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (mutex_B_locked || (mutex_A_locked || B == 5))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (mutex_A_locked || A == 5)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || ((0 <= B && B <= 127) && B != 0)' + type: assertion + format: C + +Flow-insensitive invariants as location invariants. + + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' --enable witness.invariant.flow_insensitive-as-location 04-priv_multi.c + [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) + [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) + [Warning][Deadcode] Function 'dispose' has dead code: + on line 53 (04-priv_multi.c:53-53) + on line 56 (04-priv_multi.c:56-56) + [Warning][Deadcode] Function 'process' has dead code: + on line 37 (04-priv_multi.c:37-37) + on line 40 (04-priv_multi.c:40-40) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 40 + dead: 4 + total lines: 44 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:25:10-25:11) + [Warning][Deadcode][CWE-571] condition 'A > 0' is always true (04-priv_multi.c:27:9-27:14) + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:45:10-45:11) + [Warning][Deadcode][CWE-571] condition 'B > 0' is always true (04-priv_multi.c:47:9-47:14) + [Info][Witness] witness generation summary: + total generation entries: 25 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + + $ yamlWitnessStrip < witness.yml > witness.location.yml + +Location invariant at `for` loop in `main` should be on column 3, not 7. + + $ diff witness.flow_insensitive.yml witness.location.yml + 133,134c133,140 + < - entry_type: flow_insensitive_invariant + < flow_insensitive_invariant: + --- + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 67 + > column: 7 + > function: main + > location_invariant: + 138,139c144,151 + < - entry_type: flow_insensitive_invariant + < flow_insensitive_invariant: + --- + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 67 + > column: 7 + > function: main + > location_invariant: + 143,144c155,228 + < - entry_type: flow_insensitive_invariant + < flow_insensitive_invariant: + --- + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 67 + > column: 7 + > function: main + > location_invariant: + > string: '! multithreaded || ((0 <= B && B <= 127) && B != 0)' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 65 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || (mutex_B_locked || (mutex_A_locked || B == 5))' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 65 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || (mutex_A_locked || A == 5)' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 65 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || ((0 <= B && B <= 127) && B != 0)' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 64 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || (mutex_B_locked || (mutex_A_locked || B == 5))' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 64 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || (mutex_A_locked || A == 5)' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 64 + > column: 3 + > function: main + > location_invariant: + [1] From 10dfba1437956105d7a5a91c3f3bf410ebbc9b36 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 25 Apr 2024 10:29:53 +0300 Subject: [PATCH 086/566] Fix witness.invariant.flow_insensitive-as-location at loop node --- src/witness/yamlWitness.ml | 18 ++++++++++-------- tests/regression/13-privatized/04-priv_multi.t | 6 +++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 49fe889c22..b7bf11a31c 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -342,14 +342,16 @@ struct let invs = WitnessUtil.InvariantExp.process_exp inv in Queries.NS.fold (fun n acc -> let fundec = Node.find_fundec n in - let loc = Node.location n in - let location_function = fundec.svar.vname in - let location = Entry.location ~location:loc ~location_function in - List.fold_left (fun acc inv -> - let invariant = Entry.invariant (CilType.Exp.show inv) in - let entry = Entry.location_invariant ~task ~location ~invariant in - entry :: acc - ) acc invs + match WitnessInvariant.location_location n with (* if after thread create node happens to be loop node *) + | Some loc -> + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in + List.fold_left (fun acc inv -> + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.location_invariant ~task ~location ~invariant in + entry :: acc + ) acc invs + | None -> acc ) ns acc | `Bot, _ | `Top, _ -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) acc diff --git a/tests/regression/13-privatized/04-priv_multi.t b/tests/regression/13-privatized/04-priv_multi.t index 9bdf8ac5a7..952696a5c4 100644 --- a/tests/regression/13-privatized/04-priv_multi.t +++ b/tests/regression/13-privatized/04-priv_multi.t @@ -213,7 +213,7 @@ Location invariant at `for` loop in `main` should be on column 3, not 7. > file_name: 04-priv_multi.c > file_hash: $FILE_HASH > line: 67 - > column: 7 + > column: 3 > function: main > location_invariant: 138,139c144,151 @@ -225,7 +225,7 @@ Location invariant at `for` loop in `main` should be on column 3, not 7. > file_name: 04-priv_multi.c > file_hash: $FILE_HASH > line: 67 - > column: 7 + > column: 3 > function: main > location_invariant: 143,144c155,228 @@ -237,7 +237,7 @@ Location invariant at `for` loop in `main` should be on column 3, not 7. > file_name: 04-priv_multi.c > file_hash: $FILE_HASH > line: 67 - > column: 7 + > column: 3 > function: main > location_invariant: > string: '! multithreaded || ((0 <= B && B <= 127) && B != 0)' From 16c97fde01a00b5ae8892bc8de0dea5c1070e298 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 30 Apr 2024 12:56:37 +0300 Subject: [PATCH 087/566] Add cram test for relational mutex-meet flow-insensitive invariants --- .../regression/36-apron/12-traces-min-rpb1.t | 98 +++++++++++++++++++ tests/regression/36-apron/dune | 3 + 2 files changed, 101 insertions(+) create mode 100644 tests/regression/36-apron/12-traces-min-rpb1.t diff --git a/tests/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t new file mode 100644 index 0000000000..13cefb9557 --- /dev/null +++ b/tests/regression/36-apron/12-traces-min-rpb1.t @@ -0,0 +1,98 @@ + $ goblint --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 12-traces-min-rpb1.c + [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:16:3-16:26) + [Warning][Assert] Assertion "g == h" is unknown. (12-traces-min-rpb1.c:27:3-27:26) + [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:29:3-29:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 18 + dead: 0 + total lines: 18 + [Warning][Race] Memory location h (race with conf. 110): (12-traces-min-rpb1.c:8:5-8:10) + write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & h) (12-traces-min-rpb1.c:15:3-15:8) + read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & h) (12-traces-min-rpb1.c:27:3-27:26) + [Warning][Race] Memory location g (race with conf. 110): (12-traces-min-rpb1.c:7:5-7:10) + write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:14:3-14:8) + read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:27:3-27:26) + [Info][Witness] witness generation summary: + total generation entries: 9 + [Info][Race] Memory locations race summary: + safe: 0 + vulnerable: 0 + unsafe: 2 + total memory locations: 2 + +TODO: emit g == h + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 25 + column: 3 + function: main + - entry_type: ghost_update + variable: A_locked + expression: "1" + location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + - entry_type: ghost_update + variable: A_locked + expression: "1" + location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 18 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: A_locked + expression: "1" + location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: A_locked + expression: "0" + location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 30 + column: 3 + function: main + - entry_type: ghost_update + variable: A_locked + expression: "0" + location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 19 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: A_locked + expression: "0" + location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: A_locked + scope: global + type: int + initial: "0" diff --git a/tests/regression/36-apron/dune b/tests/regression/36-apron/dune index 099ec878b2..b14ebdfe64 100644 --- a/tests/regression/36-apron/dune +++ b/tests/regression/36-apron/dune @@ -8,3 +8,6 @@ (glob_files ??-*.c)) (locks /update_suite) (action (chdir ../../.. (run %{update_suite} group apron)))) + +(cram + (deps (glob_files *.c))) From 5650784effc4c077a7f7efed045c201f42b5748b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 30 Apr 2024 13:04:36 +0300 Subject: [PATCH 088/566] Add InvariantGlobal interface to relational privatizations --- src/analyses/apron/relationAnalysis.apron.ml | 13 +++++++++++++ src/analyses/apron/relationPriv.apron.ml | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index f1ea72d0a1..ba5b90525c 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -634,6 +634,16 @@ struct ) |> Enum.fold (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none + let query_invariant_global ctx g = + (* TODO: option? *) + if ctx.ask (GhostVarAvailable Multithreaded) then ( + let var = WitnessGhost.to_varinfo Multithreaded in + let inv = Priv.invariant_global (Analyses.ask_of_ctx ctx) ctx.global g in + Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) + else + Invariant.none + let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in let st = ctx.local in @@ -655,6 +665,9 @@ struct let vf' x = vf (Obj.repr x) in Priv.iter_sys_vars ctx.global vq vf' | Queries.Invariant context -> query_invariant ctx context + | Queries.InvariantGlobal g -> + let g: V.t = Obj.obj g in + query_invariant_global ctx g | _ -> Result.top q diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 66548c117c..f286287dbe 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -46,6 +46,9 @@ module type S = val thread_return: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> ThreadIdDomain.Thread.t -> relation_components_t -> relation_components_t val iter_sys_vars: (V.t -> G.t) -> VarQuery.t -> V.t VarQuery.f -> unit (** [Queries.IterSysVars] for apron. *) + val invariant_global: Q.ask -> (V.t -> G.t) -> V.t -> Invariant.t + (** Returns flow-insensitive invariant for global unknown. *) + val invariant_vars: Q.ask -> (V.t -> G.t) -> relation_components_t -> varinfo list (** Returns global variables which are privatized. *) @@ -130,6 +133,7 @@ struct {rel = RD.top (); priv = startstate ()} let iter_sys_vars getg vq vf = () + let invariant_global ask getg g = Invariant.none let invariant_vars ask getg st = [] let init () = () @@ -410,6 +414,7 @@ struct {rel = getg (); priv = startstate ()} let iter_sys_vars getg vq vf = () (* TODO: or report singleton global for any Global query? *) + let invariant_global ask getg g = Invariant.none let invariant_vars ask getg st = protected_vars ask (* TODO: is this right? *) let finalize () = () @@ -684,6 +689,8 @@ struct let init () = () let finalize () = () + + let invariant_global ask getg g = Invariant.none (* TODO: implement *) end (** May written variables. *) @@ -1242,6 +1249,8 @@ struct | _ -> () let finalize () = () + + let invariant_global ask getg g = Invariant.none end module TracingPriv = functor (Priv: S) -> functor (RD: RelationDomain.RD) -> From d3565cb8a7903e8604ba89df0867aba8813e4f53 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 30 Apr 2024 13:42:58 +0300 Subject: [PATCH 089/566] Implement relational mutex-meet flow-insensitive invariants --- src/analyses/apron/relationPriv.apron.ml | 19 ++++++++++++++++++- .../regression/36-apron/12-traces-min-rpb1.t | 12 ++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index f286287dbe..a75e2ef113 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -690,7 +690,24 @@ struct let init () = () let finalize () = () - let invariant_global ask getg g = Invariant.none (* TODO: implement *) + let invariant_global (ask: Q.ask) (getg: V.t -> G.t): V.t -> Invariant.t = function + | `Left m' as m -> (* mutex *) + if ask.f (GhostVarAvailable (Locked m')) then ( + let cpa = getg m in + let inv = + RD.invariant cpa + (* TODO: filters like query_invariant? *) + |> List.filter_map RD.cil_exp_of_lincons1 + |> List.fold_left (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none + (* TODO: need to filter for MustBeProtectedBy like base mutex-meet? *) + in + let var = WitnessGhost.to_varinfo (Locked m') in + Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) + else + Invariant.none + | g -> (* global *) + Invariant.none (* TODO: ? *) end (** May written variables. *) diff --git a/tests/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t index 13cefb9557..e05840429b 100644 --- a/tests/regression/36-apron/12-traces-min-rpb1.t +++ b/tests/regression/36-apron/12-traces-min-rpb1.t @@ -1,4 +1,4 @@ - $ goblint --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 12-traces-min-rpb1.c + $ goblint --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 12-traces-min-rpb1.c --enable ana.apron.invariant.diff-box [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:16:3-16:26) [Warning][Assert] Assertion "g == h" is unknown. (12-traces-min-rpb1.c:27:3-27:26) [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:29:3-29:26) @@ -13,15 +13,13 @@ write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:14:3-14:8) read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:27:3-27:26) [Info][Witness] witness generation summary: - total generation entries: 9 + total generation entries: 10 [Info][Race] Memory locations race summary: safe: 0 vulnerable: 0 unsafe: 2 total memory locations: 2 -TODO: emit g == h - $ yamlWitnessStrip < witness.yml - entry_type: ghost_update variable: multithreaded @@ -96,3 +94,9 @@ TODO: emit g == h scope: global type: int initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (A_locked || ((0LL - (long long )g) + (long long )h + >= 0LL && (long long )g - (long long )h >= 0LL))' + type: assertion + format: C From 1aa35d85b74c24e14c471b7d174dc441cccdbd72 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 30 Apr 2024 14:46:48 +0300 Subject: [PATCH 090/566] Filter relational mutex-meet ghost invariant with keep_only_protected_globals lock does it too, so let's be safe. --- src/analyses/apron/relationPriv.apron.ml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index a75e2ef113..b73319b4df 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -693,13 +693,12 @@ struct let invariant_global (ask: Q.ask) (getg: V.t -> G.t): V.t -> Invariant.t = function | `Left m' as m -> (* mutex *) if ask.f (GhostVarAvailable (Locked m')) then ( - let cpa = getg m in + let rel = keep_only_protected_globals ask m' (getg m) in let inv = - RD.invariant cpa + RD.invariant rel (* TODO: filters like query_invariant? *) |> List.filter_map RD.cil_exp_of_lincons1 |> List.fold_left (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none - (* TODO: need to filter for MustBeProtectedBy like base mutex-meet? *) in let var = WitnessGhost.to_varinfo (Locked m') in Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) From c4a8936a2bd36d88cdc2909872aa6e6634098653 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 30 Apr 2024 14:53:57 +0300 Subject: [PATCH 091/566] Add filters to relational InvariantGlobal --- src/analyses/apron/relationAnalysis.apron.ml | 3 +-- src/analyses/apron/relationPriv.apron.ml | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index ba5b90525c..397301b7bc 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -635,8 +635,7 @@ struct |> Enum.fold (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none let query_invariant_global ctx g = - (* TODO: option? *) - if ctx.ask (GhostVarAvailable Multithreaded) then ( + if GobConfig.get_bool "ana.relation.invariant.global" && ctx.ask (GhostVarAvailable Multithreaded) then ( let var = WitnessGhost.to_varinfo Multithreaded in let inv = Priv.invariant_global (Analyses.ask_of_ctx ctx) ctx.global g in Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index b73319b4df..dcb3b166c3 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -693,12 +693,24 @@ struct let invariant_global (ask: Q.ask) (getg: V.t -> G.t): V.t -> Invariant.t = function | `Left m' as m -> (* mutex *) if ask.f (GhostVarAvailable (Locked m')) then ( + (* filters like query_invariant *) + let one_var = GobConfig.get_bool "ana.relation.invariant.one-var" in + let exact = GobConfig.get_bool "witness.invariant.exact" in + let rel = keep_only_protected_globals ask m' (getg m) in let inv = RD.invariant rel - (* TODO: filters like query_invariant? *) - |> List.filter_map RD.cil_exp_of_lincons1 - |> List.fold_left (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none + |> List.enum + |> Enum.filter_map (fun (lincons1: Apron.Lincons1.t) -> + (* filter one-vars and exact *) + (* TODO: exact filtering doesn't really work with octagon because it returns two SUPEQ constraints instead *) + if (one_var || GobApron.Lincons1.num_vars lincons1 >= 2) && (exact || Apron.Lincons1.get_typ lincons1 <> EQ) then + RD.cil_exp_of_lincons1 lincons1 + |> Option.filter (fun exp -> not (InvariantCil.exp_contains_tmp exp)) + else + None + ) + |> Enum.fold (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none in let var = WitnessGhost.to_varinfo (Locked m') in Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) From 535de765401c7a865c5c44be6b06f4aff85a7b65 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 7 May 2024 15:52:17 +0300 Subject: [PATCH 092/566] Add test with __VERIFIER_atomic_locked ghost variable --- tests/regression/29-svcomp/16-atomic_priv.t | 92 +++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/tests/regression/29-svcomp/16-atomic_priv.t b/tests/regression/29-svcomp/16-atomic_priv.t index 15425f68dd..d3826d8de3 100644 --- a/tests/regression/29-svcomp/16-atomic_priv.t +++ b/tests/regression/29-svcomp/16-atomic_priv.t @@ -81,3 +81,95 @@ string: '! multithreaded || myglobal == 5' type: assertion format: C + +Non-atomic privatization: + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 16-atomic_priv.c + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:12:3-12:33) + [Success][Assert] Assertion "myglobal == 6" will succeed (16-atomic_priv.c:14:3-14:33) + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:16:3-16:33) + [Warning][Assert] Assertion "myglobal == 5" is unknown. (16-atomic_priv.c:24:3-24:33) + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:26:3-26:33) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 17 + dead: 0 + total lines: 17 + [Warning][Race] Memory location myglobal (race with conf. 110): (16-atomic_priv.c:8:5-8:17) + write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:13:3-13:13) + write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:15:3-15:13) + read with [mhp:{created={[main, t_fun@16-atomic_priv.c:23:3-23:40]}}, thread:[main]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:24:3-24:33) + [Info][Witness] witness generation summary: + total generation entries: 9 + [Info][Race] Memory locations race summary: + safe: 0 + vulnerable: 0 + unsafe: 1 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + - entry_type: ghost_update + variable: __VERIFIER_atomic_locked + expression: "1" + location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 25 + column: 3 + function: main + - entry_type: ghost_update + variable: __VERIFIER_atomic_locked + expression: "1" + location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: __VERIFIER_atomic_locked + expression: "0" + location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 27 + column: 3 + function: main + - entry_type: ghost_update + variable: __VERIFIER_atomic_locked + expression: "0" + location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: __VERIFIER_atomic_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (__VERIFIER_atomic_locked || myglobal == 5)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || ((0 <= myglobal && myglobal <= 127) && myglobal != + 0)' + type: assertion + format: C From 6f3b6fbc6ab0e18560f6cbf5a409b46a6055022d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 7 May 2024 16:13:16 +0300 Subject: [PATCH 093/566] Treat __VERIFIER_atomic_locked as false in witnesses Others cannot observe anything else anyway. But in the atomic section could?! --- src/analyses/apron/relationPriv.apron.ml | 11 ++- src/analyses/basePriv.ml | 15 +++- src/analyses/mutexGhosts.ml | 5 +- src/witness/witnessGhostVar.ml | 2 +- tests/regression/29-svcomp/16-atomic_priv.t | 88 +-------------------- 5 files changed, 26 insertions(+), 95 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index dcb3b166c3..f14700f437 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -692,7 +692,8 @@ struct let invariant_global (ask: Q.ask) (getg: V.t -> G.t): V.t -> Invariant.t = function | `Left m' as m -> (* mutex *) - if ask.f (GhostVarAvailable (Locked m')) then ( + let atomic = LockDomain.Addr.equal m' (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) in + if atomic || ask.f (GhostVarAvailable (Locked m')) then ( (* filters like query_invariant *) let one_var = GobConfig.get_bool "ana.relation.invariant.one-var" in let exact = GobConfig.get_bool "witness.invariant.exact" in @@ -712,8 +713,12 @@ struct ) |> Enum.fold (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none in - let var = WitnessGhost.to_varinfo (Locked m') in - Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + if atomic then + inv + else ( + let var = WitnessGhost.to_varinfo (Locked m') in + Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) ) else Invariant.none diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 799290c4fe..af88cfb742 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -333,7 +333,8 @@ struct let invariant_global (ask: Q.ask) getg = function | `Left m' as m -> (* mutex *) - if ask.f (GhostVarAvailable (Locked m')) then ( + let atomic = LockDomain.Addr.equal m' (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) in + if atomic || ask.f (GhostVarAvailable (Locked m')) then ( let cpa = getg m in let inv = CPA.fold (fun v _ acc -> if ask.f (MustBeProtectedBy {mutex = m'; global = v; write = true; protection = Strong}) then @@ -343,8 +344,12 @@ struct acc ) cpa Invariant.none in - let var = WitnessGhost.to_varinfo (Locked m') in - Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + if atomic then + inv + else ( + let var = WitnessGhost.to_varinfo (Locked m') in + Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) ) else Invariant.none @@ -864,7 +869,9 @@ struct else ( let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) Q.AD.fold (fun m acc -> - if ask.f (GhostVarAvailable (Locked m)) then ( + if LockDomain.Addr.equal m (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) then + acc + else if ask.f (GhostVarAvailable (Locked m)) then ( let var = WitnessGhost.to_varinfo (Locked m) in Invariant.(of_exp (Lval (GoblintCil.var var)) || acc) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 75195e4662..eaa15df8e6 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -59,8 +59,9 @@ struct end let event ctx e octx = + let verifier_atomic_addr = LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var in begin match e with - | Events.Lock (l, _) -> + | Events.Lock (l, _) when not (LockDomain.Addr.equal l verifier_atomic_addr) -> ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ())); if !AnalysisState.postsolving then ( let (locked, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in @@ -70,7 +71,7 @@ struct ) locked ); ) - | Events.Unlock l -> + | Events.Unlock l when not (LockDomain.Addr.equal l verifier_atomic_addr) -> ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ())); if !AnalysisState.postsolving then ( let (_, unlocked, _) = G.node (ctx.global (V.node ctx.prev_node)) in diff --git a/src/witness/witnessGhostVar.ml b/src/witness/witnessGhostVar.ml index cec61b0e2d..7979d23173 100644 --- a/src/witness/witnessGhostVar.ml +++ b/src/witness/witnessGhostVar.ml @@ -9,7 +9,7 @@ let name_varinfo = function | Locked (Addr (v, os)) -> let name = if CilType.Varinfo.equal v LibraryFunctions.verifier_atomic_var then - "__VERIFIER_atomic" + invalid_arg "__VERIFIER_atomic" else if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo v then Printf.sprintf "alloc_%s%d" (if v.vid < 0 then "m" else "") (abs v.vid) (* turn minus into valid C name *) diff --git a/tests/regression/29-svcomp/16-atomic_priv.t b/tests/regression/29-svcomp/16-atomic_priv.t index d3826d8de3..b10265d4e8 100644 --- a/tests/regression/29-svcomp/16-atomic_priv.t +++ b/tests/regression/29-svcomp/16-atomic_priv.t @@ -13,7 +13,7 @@ write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:15:3-15:13) read with [mhp:{created={[main, t_fun@16-atomic_priv.c:23:3-23:40]}}, thread:[main]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:24:3-24:33) [Info][Witness] witness generation summary: - total generation entries: 8 + total generation entries: 3 [Info][Race] Memory locations race summary: safe: 0 vulnerable: 0 @@ -30,52 +30,11 @@ line: 23 column: 3 function: main - - entry_type: ghost_update - variable: __VERIFIER_atomic_locked - expression: "1" - location: - file_name: 16-atomic_priv.c - file_hash: $FILE_HASH - line: 25 - column: 3 - function: main - - entry_type: ghost_update - variable: __VERIFIER_atomic_locked - expression: "1" - location: - file_name: 16-atomic_priv.c - file_hash: $FILE_HASH - line: 11 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: __VERIFIER_atomic_locked - expression: "0" - location: - file_name: 16-atomic_priv.c - file_hash: $FILE_HASH - line: 27 - column: 3 - function: main - - entry_type: ghost_update - variable: __VERIFIER_atomic_locked - expression: "0" - location: - file_name: 16-atomic_priv.c - file_hash: $FILE_HASH - line: 17 - column: 3 - function: t_fun - entry_type: ghost_variable variable: multithreaded scope: global type: int initial: "0" - - entry_type: ghost_variable - variable: __VERIFIER_atomic_locked - scope: global - type: int - initial: "0" - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || myglobal == 5' @@ -99,7 +58,7 @@ Non-atomic privatization: write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:15:3-15:13) read with [mhp:{created={[main, t_fun@16-atomic_priv.c:23:3-23:40]}}, thread:[main]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:24:3-24:33) [Info][Witness] witness generation summary: - total generation entries: 9 + total generation entries: 4 [Info][Race] Memory locations race summary: safe: 0 vulnerable: 0 @@ -116,55 +75,14 @@ Non-atomic privatization: line: 23 column: 3 function: main - - entry_type: ghost_update - variable: __VERIFIER_atomic_locked - expression: "1" - location: - file_name: 16-atomic_priv.c - file_hash: $FILE_HASH - line: 25 - column: 3 - function: main - - entry_type: ghost_update - variable: __VERIFIER_atomic_locked - expression: "1" - location: - file_name: 16-atomic_priv.c - file_hash: $FILE_HASH - line: 11 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: __VERIFIER_atomic_locked - expression: "0" - location: - file_name: 16-atomic_priv.c - file_hash: $FILE_HASH - line: 27 - column: 3 - function: main - - entry_type: ghost_update - variable: __VERIFIER_atomic_locked - expression: "0" - location: - file_name: 16-atomic_priv.c - file_hash: $FILE_HASH - line: 17 - column: 3 - function: t_fun - entry_type: ghost_variable variable: multithreaded scope: global type: int initial: "0" - - entry_type: ghost_variable - variable: __VERIFIER_atomic_locked - scope: global - type: int - initial: "0" - entry_type: flow_insensitive_invariant flow_insensitive_invariant: - string: '! multithreaded || (__VERIFIER_atomic_locked || myglobal == 5)' + string: '! multithreaded || myglobal == 5' type: assertion format: C - entry_type: flow_insensitive_invariant From 2e6673f72e1e3df8c67b9c0c21aec9f45e1c4ab2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 8 May 2024 11:41:45 +0300 Subject: [PATCH 094/566] Disable 13-privatized/04-priv_multi cram test on OSX OSX has its own weird diff. --- tests/regression/13-privatized/dune | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/regression/13-privatized/dune b/tests/regression/13-privatized/dune index 23c0dd3290..9227128b15 100644 --- a/tests/regression/13-privatized/dune +++ b/tests/regression/13-privatized/dune @@ -1,2 +1,6 @@ (cram (deps (glob_files *.c))) + +(cram + (applies_to 04-priv_multi) + (enabled_if (<> %{system} macosx))) From b7582a4c5495e35a24974228785817b01711a37c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 8 May 2024 11:43:03 +0300 Subject: [PATCH 095/566] Make 36-apron/12-traces-min-rpb1 cram test warnings deterministic Needed for OSX CI to pass. --- .../regression/36-apron/12-traces-min-rpb1.t | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t index e05840429b..7aca1dea0b 100644 --- a/tests/regression/36-apron/12-traces-min-rpb1.t +++ b/tests/regression/36-apron/12-traces-min-rpb1.t @@ -1,24 +1,24 @@ - $ goblint --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 12-traces-min-rpb1.c --enable ana.apron.invariant.diff-box - [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:16:3-16:26) + $ goblint --enable warn.deterministic --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 12-traces-min-rpb1.c --enable ana.apron.invariant.diff-box [Warning][Assert] Assertion "g == h" is unknown. (12-traces-min-rpb1.c:27:3-27:26) + [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:16:3-16:26) [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:29:3-29:26) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 18 - dead: 0 - total lines: 18 - [Warning][Race] Memory location h (race with conf. 110): (12-traces-min-rpb1.c:8:5-8:10) - write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & h) (12-traces-min-rpb1.c:15:3-15:8) - read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & h) (12-traces-min-rpb1.c:27:3-27:26) [Warning][Race] Memory location g (race with conf. 110): (12-traces-min-rpb1.c:7:5-7:10) write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:14:3-14:8) read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:27:3-27:26) - [Info][Witness] witness generation summary: - total generation entries: 10 + [Warning][Race] Memory location h (race with conf. 110): (12-traces-min-rpb1.c:8:5-8:10) + write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & h) (12-traces-min-rpb1.c:15:3-15:8) + read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & h) (12-traces-min-rpb1.c:27:3-27:26) [Info][Race] Memory locations race summary: safe: 0 vulnerable: 0 unsafe: 2 total memory locations: 2 + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 18 + dead: 0 + total lines: 18 + [Info][Witness] witness generation summary: + total generation entries: 10 $ yamlWitnessStrip < witness.yml - entry_type: ghost_update From c6d19a977d7671354b4daf7bb870b295d69091b0 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 15 May 2024 12:30:01 +0200 Subject: [PATCH 096/566] initial components for coefficients added --- .../apron/linearTwoVarEqualityDomain.apron.ml | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 67bd67f4e5..ebe973536a 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -17,15 +17,20 @@ open VectorMatrix module Mpqf = SharedFunctions.Mpqf module Rhs = struct - (* (Some i, k) represents a sum of a variable with index i and the number k. - (None, k) represents the number k. *) - type t = (int option * GobZ.t) [@@deriving eq, ord, hash] - let var_zero i = (Some i, Z.zero) - let show_formatted formatter = function - | (Some v, o) when Z.equal o Z.zero -> formatter v - | (Some v, o) -> Printf.sprintf "%s%+Ld" (formatter v) (Z.to_int64 o) - | (None, o) -> Printf.sprintf "%Ld" (Z.to_int64 o) - let show rhs = show_formatted (Printf.sprintf "var_%d") rhs + (* (Some i, k,_,_) represents a sum of a variable with index i and the number k. + (None, k,_,_) represents the number k. *) + type t = (int option * GobZ.t * GobZ.t * GobZ.t) [@@deriving eq, ord, hash] + let var_zero i = (Some i, Z.zero, Z.one, Z.one) + let show_coeff c = + if Z.equal c Z.one then "" + else (Z.to_string c) ^"*" + let show_rhs_formatted formatter = function + | (Some v, o,coeff,_) when Z.equal o Z.zero -> Printf.sprintf "%s%s" (show_coeff coeff) (formatter v) + | (Some v, o,coeff,_) -> Printf.sprintf "%s%s%+Ld" (show_coeff coeff) (formatter v) (Z.to_int64 o) + | (None, o,_,_) -> Printf.sprintf "%Ld" (Z.to_int64 o) + let show (v,o,c,d) = + let rhs=show_rhs_formatted (Printf.sprintf "var_%d") (v,o,c,d) in + if not (Z.equal d Z.one) then "(" ^ rhs ^ ")/" ^ (Z.to_string d) else rhs end module EqualitiesConjunction = struct @@ -36,7 +41,7 @@ module EqualitiesConjunction = struct let show_formatted formatter econ = if IntMap.is_empty econ then "{}" else - let str = IntMap.fold (fun i (refvar,off) acc -> Printf.sprintf "%s=%s ∧ %s" (formatter i) (Rhs.show_formatted formatter (refvar,off)) acc) econ "" in + let str = IntMap.fold (fun i (refvar,off,coeff,divi) acc -> Printf.sprintf "%s%s=%s ∧ %s" (Rhs.show_coeff divi) (formatter i) (Rhs.show_rhs_formatted formatter (refvar,off,coeff,divi)) acc) econ "" in "{" ^ String.sub str 0 (String.length str - 4) ^ "}" let show econ = show_formatted (Printf.sprintf "var_%d") econ @@ -86,9 +91,9 @@ module EqualitiesConjunction = struct IntHashtbl.add h x r; r) in - let rec bumpentry k (refvar,offset) = function (* directly bumps lhs-variable during a run through indexes, bumping refvar explicitely with a new lookup in indexes *) - | (tbl,delta,head::rest) when k>=head -> bumpentry k (refvar,offset) (tbl,delta+1,rest) (* rec call even when =, in order to correctly interpret double bumps *) - | (tbl,delta,lyst) (* k (IntMap.add (op k delta) (BatOption.map (memobumpvar) refvar, offset) tbl, delta, lyst) + let rec bumpentry k (refvar,offset,coeff,divi) = function (* directly bumps lhs-variable during a run through indexes, bumping refvar explicitely with a new lookup in indexes *) + | (tbl,delta,head::rest) when k>=head -> bumpentry k (refvar,offset,coeff,divi) (tbl,delta+1,rest) (* rec call even when =, in order to correctly interpret double bumps *) + | (tbl,delta,lyst) (* k (IntMap.add (op k delta) (BatOption.map (memobumpvar) refvar,offset,coeff,divi) tbl, delta, lyst) in let (a,_,_) = IntMap.fold bumpentry map (IntMap.empty,0,offsetlist) in (* Build new map during fold with bumped key/vals *) (op dim (Array.length indexes), a) From d2ce1755e394d15e67f44c7cbc7c877ae55406ea Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 15 May 2024 13:36:49 +0200 Subject: [PATCH 097/566] adaptation of forget_variable to coefficients --- .../apron/linearTwoVarEqualityDomain.apron.ml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index ebe973536a..ffccecb05f 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -116,17 +116,23 @@ module EqualitiesConjunction = struct (* Forget information about variable i *) let forget_variable d var = let res = - (let ref_var_opt = fst (get_rhs d var) in + (let ref_var_opt = Tuple4.first (get_rhs d var) in match ref_var_opt with | Some ref_var when ref_var = var -> (* var is the reference variable of its connected component *) (let cluster = IntMap.fold - (fun i (ref, offset) l -> if ref = ref_var_opt then i::l else l) (snd d) [] in + (fun i (ref,_,_,_) l -> if ref = ref_var_opt then i::l else l) (snd d) [] in (* obtain cluster with common reference variable ref_var*) match cluster with (* new ref_var is taken from head of the cluster *) - | head :: tail -> - let headconst = snd (get_rhs d head) in (* take offset between old and new reference variable *) - List.fold (fun map i -> set_rhs map i Z.(Some head, snd (get_rhs d i) - headconst)) d cluster (* shift offset to match new reference variable *) + | head :: _ -> + (* ax = by + c /\ a'z = b'y + c' *) + (* ==[[ y:=? ]]==> (a'b)z = (b'a)x + c' -(b'c) *) + let (_,c,b,a) = (get_rhs d head) in (* take offset between old and new reference variable *) + List.fold (fun map i -> + let (_,c',b',a') = (get_rhs d i) in + let newrhs = (Some head, Z.(c' - (b' * c)), Z.(b'*a), Z.(a'*b)) in + set_rhs map i newrhs + ) d cluster (* shift offset to match new reference variable *) | [] -> d) (* empty cluster means no work for us *) | _ -> d) (* variable is either a constant or expressed by another refvar *) in let res = (fst res, IntMap.remove var (snd res)) in (* set d(var) to unknown, finally *) From 889161995918736447ff54803286b26d040d88cb Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Wed, 15 May 2024 17:20:55 +0200 Subject: [PATCH 098/566] more accurate representation and canonicalization --- .../apron/linearTwoVarEqualityDomain.apron.ml | 59 ++++++++++++------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index ffccecb05f..8fb799804f 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -17,20 +17,30 @@ open VectorMatrix module Mpqf = SharedFunctions.Mpqf module Rhs = struct - (* (Some i, k,_,_) represents a sum of a variable with index i and the number k. - (None, k,_,_) represents the number k. *) - type t = (int option * GobZ.t * GobZ.t * GobZ.t) [@@deriving eq, ord, hash] - let var_zero i = (Some i, Z.zero, Z.one, Z.one) + (* Rhs represents coefficient*var_i + offset / divisor + depending on whether coefficient is 0, the monomial term may disappear completely, not refering to any var_i, thus: + (Some (coefficient, i), offset, divisor ) with coefficient != 0 , or + (None , offset, divisor ) *) + type t = ((GobZ.t * int) option * GobZ.t * GobZ.t) [@@deriving eq, ord, hash] + let var_zero i = (Some (Z.one,i), Z.zero, Z.one) let show_coeff c = if Z.equal c Z.one then "" else (Z.to_string c) ^"*" let show_rhs_formatted formatter = function - | (Some v, o,coeff,_) when Z.equal o Z.zero -> Printf.sprintf "%s%s" (show_coeff coeff) (formatter v) - | (Some v, o,coeff,_) -> Printf.sprintf "%s%s%+Ld" (show_coeff coeff) (formatter v) (Z.to_int64 o) - | (None, o,_,_) -> Printf.sprintf "%Ld" (Z.to_int64 o) - let show (v,o,c,d) = - let rhs=show_rhs_formatted (Printf.sprintf "var_%d") (v,o,c,d) in + | (Some (coeff,v), o,_) when Z.equal o Z.zero -> Printf.sprintf "%s%s" (show_coeff coeff) (formatter v) + | (Some (coeff,v), o,_) -> Printf.sprintf "%s%s%+Ld" (show_coeff coeff) (formatter v) (Z.to_int64 o) + | (None, o,_) -> Printf.sprintf "%Ld" (Z.to_int64 o) + let show (v,o,d) = + let rhs=show_rhs_formatted (Printf.sprintf "var_%d") (v,o,d) in if not (Z.equal d Z.one) then "(" ^ rhs ^ ")/" ^ (Z.to_string d) else rhs + + (** factor out gcd from all terms, i.e. ax=by+c is the canonical form for adx+bdy+cd *) + let canonicalize (v,o,d) = + let gcd = Z.gcd o d in + let gcd = match v with + | Some (c,_) -> Z.gcd c gcd + | None -> gcd + in (BatOption.map (fun (coeff,i) -> (Z.div coeff gcd,i)) v,Z.div o gcd, Z.div d gcd) end module EqualitiesConjunction = struct @@ -41,7 +51,7 @@ module EqualitiesConjunction = struct let show_formatted formatter econ = if IntMap.is_empty econ then "{}" else - let str = IntMap.fold (fun i (refvar,off,coeff,divi) acc -> Printf.sprintf "%s%s=%s ∧ %s" (Rhs.show_coeff divi) (formatter i) (Rhs.show_rhs_formatted formatter (refvar,off,coeff,divi)) acc) econ "" in + let str = IntMap.fold (fun i (ref,off,divi) acc -> Printf.sprintf "%s%s=%s ∧ %s" (Rhs.show_coeff divi) (formatter i) (Rhs.show_rhs_formatted formatter (ref,off,divi)) acc) econ "" in "{" ^ String.sub str 0 (String.length str - 4) ^ "}" let show econ = show_formatted (Printf.sprintf "var_%d") econ @@ -60,13 +70,18 @@ module EqualitiesConjunction = struct (** sparse implementation of get rhs for lhs, but will default to no mapping for sparse entries *) let get_rhs (_,econmap) lhs = IntMap.find_default (Rhs.var_zero lhs) lhs econmap - (** set_rhs, staying loyal to immutable, sparse map underneath *) + (** set_rhs, staying loyal to immutable, sparse map underneath; do not attempt any normalization *) let set_rhs (dim,map) lhs rhs = (dim, if Rhs.equal rhs Rhs.(var_zero lhs) then IntMap.remove lhs map else IntMap.add lhs rhs map ) + + (** canonicalize equation, and set_rhs, staying loyal to immutable, sparse map underneath,*) + let canonicalize_and_set (dim,map) lhs rhs = set_rhs (dim,map) lhs (Rhs.canonicalize rhs) + + (** add a new equality to the domain *) let copy = identity @@ -91,9 +106,9 @@ module EqualitiesConjunction = struct IntHashtbl.add h x r; r) in - let rec bumpentry k (refvar,offset,coeff,divi) = function (* directly bumps lhs-variable during a run through indexes, bumping refvar explicitely with a new lookup in indexes *) - | (tbl,delta,head::rest) when k>=head -> bumpentry k (refvar,offset,coeff,divi) (tbl,delta+1,rest) (* rec call even when =, in order to correctly interpret double bumps *) - | (tbl,delta,lyst) (* k (IntMap.add (op k delta) (BatOption.map (memobumpvar) refvar,offset,coeff,divi) tbl, delta, lyst) + let rec bumpentry k (refvar,offset,divi) = function (* directly bumps lhs-variable during a run through indexes, bumping refvar explicitely with a new lookup in indexes *) + | (tbl,delta,head::rest) when k>=head -> bumpentry k (refvar,offset,divi) (tbl,delta+1,rest) (* rec call even when =, in order to correctly interpret double bumps *) + | (tbl,delta,lyst) (* k (IntMap.add (op k delta) (BatOption.map (fun (c,v) -> (c,memobumpvar v)) refvar,offset,divi) tbl, delta, lyst) in let (a,_,_) = IntMap.fold bumpentry map (IntMap.empty,0,offsetlist) in (* Build new map during fold with bumped key/vals *) (op dim (Array.length indexes), a) @@ -116,22 +131,24 @@ module EqualitiesConjunction = struct (* Forget information about variable i *) let forget_variable d var = let res = - (let ref_var_opt = Tuple4.first (get_rhs d var) in + (let ref_var_opt = Tuple3.first (get_rhs d var) in match ref_var_opt with - | Some ref_var when ref_var = var -> + | Some (_,ref_var) when ref_var = var -> (* var is the reference variable of its connected component *) (let cluster = IntMap.fold - (fun i (ref,_,_,_) l -> if ref = ref_var_opt then i::l else l) (snd d) [] in + (fun i (ref,_,_) l -> if ref = ref_var_opt then i::l else l) (snd d) [] in (* obtain cluster with common reference variable ref_var*) match cluster with (* new ref_var is taken from head of the cluster *) | head :: _ -> (* ax = by + c /\ a'z = b'y + c' *) (* ==[[ y:=? ]]==> (a'b)z = (b'a)x + c' -(b'c) *) - let (_,c,b,a) = (get_rhs d head) in (* take offset between old and new reference variable *) + let (newref,c,a) = (get_rhs d head) in (* take offset between old and new reference variable *) + let (b,_) = BatOption.get newref in List.fold (fun map i -> - let (_,c',b',a') = (get_rhs d i) in - let newrhs = (Some head, Z.(c' - (b' * c)), Z.(b'*a), Z.(a'*b)) in - set_rhs map i newrhs + let (oldref,c',a') = (get_rhs d i) in + let (b',_) = BatOption.get oldref in + let newrhs = (Some (Z.(b'*a),head), Z.(c' - (b' * c)), Z.(a'*b)) in + canonicalize_and_set map i newrhs ) d cluster (* shift offset to match new reference variable *) | [] -> d) (* empty cluster means no work for us *) | _ -> d) (* variable is either a constant or expressed by another refvar *) in From 19c43f6f7e9b78cfe7fd0015f25b86e60743739e Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Wed, 15 May 2024 17:55:37 +0200 Subject: [PATCH 099/566] conversion to lincons --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 8fb799804f..6fa7d9423d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -725,14 +725,14 @@ struct lincons in let get_const acc i = function - | (None, o) -> + | (None, o, d) -> let xi = Environment.var_of_dim t.env i in - of_coeff xi [(Coeff.s_of_int (-1), xi)] o :: acc - | (Some r, _) when r = i -> acc - | (Some r, o) -> + of_coeff xi [(Coeff.s_of_int (- (Z.to_int d)), xi)] o :: acc + | (Some (c,r), _,_) when r = i -> acc + | (Some (c,r), o, d) -> let xi = Environment.var_of_dim t.env i in let ri = Environment.var_of_dim t.env r in - of_coeff xi [(Coeff.s_of_int (-1), xi); (Coeff.s_of_int 1, ri)] o :: acc + of_coeff xi [(Coeff.s_of_int (- (Z.to_int d)), xi); (Coeff.s_of_int @@ Z.to_int c, ri)] o :: acc in BatOption.get t.d |> fun (_,map) -> EConj.IntMap.fold (fun lhs rhs list -> get_const list lhs rhs) map [] From 5c2e7410da4d996ecdb255cdc0a00c5b4063242d Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Thu, 16 May 2024 09:25:08 +0200 Subject: [PATCH 100/566] [skip ci] generate coefficient-carrying monomials from texp --- .../apron/linearTwoVarEqualityDomain.apron.ml | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 6fa7d9423d..620dd307b2 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -223,14 +223,17 @@ struct let open Apron.Texpr1 in let exception NotLinearExpr in let exception ScalarIsInfinity in - let negate coeff_var_list = List.map (fun (coeff, var) -> (Z.(-coeff), var)) coeff_var_list in - let multiply_with_Z number coeff_var_list = - List.map (fun (coeff, var) -> (Z.(number * coeff, var))) coeff_var_list in + let negate coeff_var_list = List.map (function + | (Some(coeff,i),offs,divi) -> (Some(Z.neg coeff,i),Z.neg offs,divi) + | (None ,offs,divi) -> (None ,Z.neg offs,divi)) coeff_var_list in + let multiply_with_Z number divisor coeff_var_list = List.map (function + | (Some (coeff, var),offs,divi) -> Rhs.canonicalize (Some(Z.mul number coeff,var),Z.(number * offs),Z.mul divi divisor) + | (None,offs,divi) -> Rhs.canonicalize (None,Z.mul number offs,Z.mul divi divisor)) coeff_var_list in let multiply a b = (* if one of them is a constant, then multiply. Otherwise, the expression is not linear *) match a, b with - | [(a_coeff, None)], b -> multiply_with_Z a_coeff b - | a, [(b_coeff, None)] -> multiply_with_Z b_coeff a + | [(None,a_coeff, divi)], b -> multiply_with_Z a_coeff divi b + | a, [(None,b_coeff, divi)] -> multiply_with_Z b_coeff divi a | _ -> raise NotLinearExpr in let rec convert_texpr texp = @@ -239,16 +242,16 @@ struct | Cst (Interval _) -> failwith "constant was an interval; this is not supported" | Cst (Scalar x) -> begin match SharedFunctions.int_of_scalar ?round:None x with - | Some x -> [(x, None)] + | Some x -> [(None,x,Z.one)] | None -> raise ScalarIsInfinity end | Var x -> let var_dim = Environment.dim_of_var t.env x in begin match t.d with - | None -> [(Z.one, Some var_dim)] + | None -> [(Some (Z.one,var_dim),Z.zero,Z.one)] | Some d -> (match (EConj.get_rhs d var_dim) with - | (Some i, k) -> [(Z.one, Some i); (k, None)] - | (None, k) -> [(k, None)]) + | (Some (coeff,i), k,divi) -> [(Some (coeff,i),Z.zero,Z.one); (None,k,Z.one)] + | (None, k,divi) -> [ (None,k,Z.one)]) end | Unop (Neg, e, _, _) -> negate (convert_texpr e) | Unop (Cast, e, _, _) -> convert_texpr e (* Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts *) From e97e26e175ab430dac47e5e92cd42212e6bc83a5 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 17 May 2024 15:28:02 +0200 Subject: [PATCH 101/566] [skip ci] migrated meet_with_one_conj --- .../apron/linearTwoVarEqualityDomain.apron.ml | 68 ++++++++++++++----- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 620dd307b2..1793f38de8 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -41,6 +41,14 @@ module Rhs = struct | Some (c,_) -> Z.gcd c gcd | None -> gcd in (BatOption.map (fun (coeff,i) -> (Z.div coeff gcd,i)) v,Z.div o gcd, Z.div d gcd) + + (** Substitute rhs for varx in rhs' *) + let subst rhs varx rhs' = + match rhs,rhs' with + | (Some (c,x),o,d),(Some (c',x'),o',d') when x'=varx -> canonicalize (Some (Z.mul c c',x),Z.((o*c')+(d*o')),Z.mul d d') + | (None ,o,d),(Some (c',x'),o',d') when x'=varx -> canonicalize (None ,Z.((o*c')+(d*o')),Z.mul d d') + | _ -> rhs' + end module EqualitiesConjunction = struct @@ -67,6 +75,9 @@ module EqualitiesConjunction = struct (** trivial equalities are of the form var_i = var_i and are not kept explicitely in the sparse representation of EquanlitiesConjunction *) let nontrivial (_,econmap) lhs = IntMap.mem lhs econmap + (** turn x = (cy+o)/d into y = (dx-o)/c*) + let reverse x (c,y,o,d) = (y,(Some (d,x),Z.neg o,c)) + (** sparse implementation of get rhs for lhs, but will default to no mapping for sparse entries *) let get_rhs (_,econmap) lhs = IntMap.find_default (Rhs.var_zero lhs) lhs econmap @@ -181,29 +192,52 @@ module EqualitiesConjunction = struct exception Contradiction - let meet_with_one_conj ts i (var, b) = + let meet_with_one_conj ts i (var, offs, divi) = + let (var,offs,divi) = Rhs.canonicalize (var,offs,divi) in (* make sure that the one new conj is properly canonicalized *) let res = - let subst_var tsi x (vart, bt) = + let subst_var tsi x (vary, o, d) = + (* [[x substby (cy+o)/d ]] ((c'x+o')/d') *) + (* =====> (c'cy + c'o+o'd)/(dd') *) let adjust = function - | (Some vare, b') when vare = x -> (vart, Z.(b' + bt)) + | (Some (c',varx), o',d') when varx = x -> Rhs.canonicalize (BatOption.map (fun (c,y)->(Z.mul c c',y)) vary, Z.((c'*o)+(o'*d)),Z.(d'*d)) | e -> e in - (fst tsi, IntMap.add x (vart, bt) @@ IntMap.map adjust (snd tsi)) (* in case of sparse representation, make sure that the equality is now included in the conjunction *) + (fst tsi, IntMap.add x (vary, o, d) @@ IntMap.map adjust (snd tsi)) (* in case of sparse representation, make sure that the equality is now included in the conjunction *) in - let (var1, b1) = get_rhs ts i in - (match var, var1 with - | None , None -> if not @@ Z.equal b b1 then raise Contradiction else ts - | None , Some h1 -> subst_var ts h1 (None, Z.(b - b1)) - | Some j, None -> subst_var ts j (None, Z.(b1 - b)) - | Some j, Some h1 -> + (match var, (get_rhs ts i) with + (*| new conj , old conj *) + | None , (None , o1, divi1) -> if not @@ (Z.equal offs o1 && Z.equal divi divi1) then raise Contradiction else ts + (* o/d = x_i = (c1*x_h1+o1)/d1 *) + (* ======> x_h1 = (o*d1-o1*d)/(d*c1) /\ x_i = o/d *) + | None , (Some (coeff1,h1), o1, divi1) -> subst_var ts h1 (None, Z.(offs*divi1 - o1*divi),Z.(divi*coeff1)) + (* (c*x_j+o)/d = x_i = o1/d1 *) + (* ======> x_j = (o1*d-o*d1)/(d1*c) /\ x_i = o1/d1 *) + | Some (coeff,j), (None , o1, divi1) -> subst_var ts j (None, Z.(o1*divi - offs*divi1),Z.(divi1*coeff)) + (* (c*x_j+o)/d = x_i = (c1*x_h1+o1)/d1 *) + (* ======> x_j needs normalization wrt. ts *) + | Some (coeff,j), ((Some (coeff1,h1), o1, divi1) as oldi)-> (match get_rhs ts j with - | (None, b2) -> subst_var ts i (None, Z.(b2 + b)) - | (Some h2, b2) -> - if h1 = h2 then - (if not @@ Z.equal b1 Z.(b2 + b) then raise Contradiction else ts) - else if h1 < h2 then subst_var ts h2 (Some h1, Z.(b1 - (b + b2))) - else subst_var ts h1 (Some h2, Z.(b + (b2 - b1))))) in - if M.tracing then M.trace "meet" "meet_with_one_conj conj: { %s } eq: var_%d=%s -> { %s } " (show (snd ts)) i (Rhs.show (var,b)) (show (snd ts)) + (* ts[x_j]=o2/d2 ========> ... *) + | (None , o2, divi2) -> + let newxi = Rhs.subst (None,o2,divi2) j (Some (coeff,j),offs,divi) in + let newxh1 = snd @@ reverse i (coeff1,h1,o1,divi1) in + let newxh1 = Rhs.subst newxi i newxh1 in + subst_var ts h1 newxh1 + (* ts[x_j]=(c2*x_h2+o2)/d2 ========> ... *) + | (Some (coeff2,h2), o2, divi2) as normalizedj -> + if h1 = h2 then (* this is the case where x_i and x_j already where in the same equivalence class; let's see whether the new equality contradicts the old one *) + let normalizedi= Rhs.subst normalizedj j (Some(coeff,j),offs,divi) in + (if not @@ Rhs.equal normalizedi oldi then raise Contradiction else ts) + else if h1 < h2 (* good, we no unite the two equvalence classes; let's decide upon the representant *) + then (* express h2 in terms of h1: *) + let (_,newh2)= reverse j (coeff2,h2,o2,divi2) in + let newh2 = Rhs.subst oldi i (Rhs.subst (snd @@ reverse i (coeff,j,offs,divi)) j newh2) in + subst_var ts h2 newh2 + else (* express h1 in terms of h2: *) + let (_,newh1)= reverse i (coeff1,h1,o1,divi1) in + let newh1 = Rhs.subst normalizedj j (Rhs.subst (Some(coeff,j),offs,divi) i newh1) in + subst_var ts h1 newh1)) in + if M.tracing then M.trace "meet" "meet_with_one_conj conj: { %s } eq: var_%d=%s -> { %s } " (show (snd ts)) i (Rhs.show (var,offs,divi)) (show (snd ts)) ; res end From a9248db7e355c7dfa74182acaa8eeab55abfb866 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 17 May 2024 15:38:23 +0200 Subject: [PATCH 102/566] [skip ci] tiny migration steps, low hanging fruit --- .../apron/linearTwoVarEqualityDomain.apron.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 1793f38de8..6d6363329b 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -327,7 +327,7 @@ struct (* Copy because function is not "with" so should not mutate inputs *) let assign_const t var const = match t.d with | None -> t - | Some t_d -> {d = Some (EConj.set_rhs t_d var (None, const)); env = t.env} + | Some t_d -> {d = Some (EConj.set_rhs t_d var (None, const,Z.one)); env = t.env} let subtract_const_from_var t var const = match t.d with @@ -419,12 +419,12 @@ struct let printXml f x = BatPrintf.fprintf f "\n\n\nequalities\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) let eval_interval ask = Bounds.bound_texpr - let meet_with_one_conj t i (var, b) = + let meet_with_one_conj t i (var, o, divi) = match t.d with | None -> t | Some d -> try - { d = Some (EConj.meet_with_one_conj d i (var, b)); env = t.env} + { d = Some (EConj.meet_with_one_conj d i (var, o, divi)); env = t.env} with EConj.Contradiction -> if M.tracing then M.trace "meet" " -> Contradiction\n"; { d = None; env = t.env} @@ -585,7 +585,7 @@ struct subtract_const_from_var t var_i off | Some (Some exp_var, off) -> (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) - meet_with_one_conj (forget_var t var) var_i (Some exp_var, off) + meet_with_one_conj (forget_var t var) var_i (Some (Z.one,exp_var), off, Z.one) end | None -> bot_env @@ -702,13 +702,13 @@ struct end | [(varexpr, index)] -> (* guard has a single reference variable only *) if Tcons1.get_typ tcons = EQ && Z.divisible constant varexpr then - meet_with_one_conj t index (None, (Z.(-(constant) / varexpr))) + meet_with_one_conj t index (None, (Z.(-(constant) / varexpr)),Z.one) else t (* only EQ is supported in equality based domains *) | [(a1,var1); (a2,var2)] -> (* two variables in relation needs a little sorting out *) begin match Tcons1.get_typ tcons with | EQ when Z.(a1 * a2 = -one) -> (* var1-var1 or var2-var1 *) - meet_with_one_conj t var2 (Some var1, Z.mul a1 constant) + meet_with_one_conj t var2 (Some (Z.one,var1), Z.mul a1 constant,Z.one) | _-> t (* Not supported in equality based 2vars without coeffiients *) end | _ -> t (* For equalities of more then 2 vars we just return t *)) From 1257b680109575bf79157d33f503eb23c7413fb1 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Sun, 19 May 2024 23:23:50 +0200 Subject: [PATCH 103/566] [skip ci] dealt with simplifying linear expressions to lin2vars with coeffs --- .../apron/linearTwoVarEqualityDomain.apron.ml | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 6d6363329b..1e60f28e8f 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -260,14 +260,14 @@ struct let negate coeff_var_list = List.map (function | (Some(coeff,i),offs,divi) -> (Some(Z.neg coeff,i),Z.neg offs,divi) | (None ,offs,divi) -> (None ,Z.neg offs,divi)) coeff_var_list in - let multiply_with_Z number divisor coeff_var_list = List.map (function - | (Some (coeff, var),offs,divi) -> Rhs.canonicalize (Some(Z.mul number coeff,var),Z.(number * offs),Z.mul divi divisor) - | (None,offs,divi) -> Rhs.canonicalize (None,Z.mul number offs,Z.mul divi divisor)) coeff_var_list in + let multiply_with_Q dividend divisor coeff_var_list = List.map (function + | (Some (coeff, var),offs,divi) -> Rhs.canonicalize (Some(Z.mul dividend coeff,var),Z.(dividend * offs),Z.mul divi divisor) + | (None,offs,divi) -> Rhs.canonicalize (None,Z.mul dividend offs,Z.mul divi divisor)) coeff_var_list in let multiply a b = (* if one of them is a constant, then multiply. Otherwise, the expression is not linear *) match a, b with - | [(None,a_coeff, divi)], b -> multiply_with_Z a_coeff divi b - | a, [(None,b_coeff, divi)] -> multiply_with_Z b_coeff divi a + | [(None,coeff, divi)], c + | c, [(None,coeff, divi)] -> multiply_with_Q coeff divi c | _ -> raise NotLinearExpr in let rec convert_texpr texp = @@ -284,8 +284,8 @@ struct | None -> [(Some (Z.one,var_dim),Z.zero,Z.one)] | Some d -> (match (EConj.get_rhs d var_dim) with - | (Some (coeff,i), k,divi) -> [(Some (coeff,i),Z.zero,Z.one); (None,k,Z.one)] - | (None, k,divi) -> [ (None,k,Z.one)]) + | (Some (coeff,i), k,divi) -> [(Some (coeff,i),Z.zero,divi); (None,k,divi)] + | (None, k,divi) -> [ (None,k,divi)]) end | Unop (Neg, e, _, _) -> negate (convert_texpr e) | Unop (Cast, e, _, _) -> convert_texpr e (* Ignore since casts in apron are used for floating point nums and rounding in contrast to CIL casts *) @@ -299,27 +299,27 @@ struct | exception ScalarIsInfinity -> None | x -> Some(x) - (** convert and simplify (wrt. reference variables) a texpr into a tuple of a list of monomials and a constant *) + (** convert and simplify (wrt. reference variables) a texpr into a tuple of a list of monomials (coeff,varidx,divi) and a (constant/divi) *) let simplified_monomials_from_texp (t: t) texp = BatOption.bind (monomials_from_texp t texp) (fun monomiallist -> let d = Option.get t.d in - let expr = Array.make (Environment.size t.env) Z.zero in - let accumulate_constants a (c, v) = match v with - | None -> Z.(a + c) - | Some idx -> let (term,con) = (EConj.get_rhs d idx) in - (Option.may (fun ter -> expr.(ter) <- Z.(expr.(ter) + c)) term; - Z.(a + c * con)) + let expr = Array.make (Environment.size t.env) (Q.zero) in (*TODO*: migrate to map; array of coeff/divisor per var idx*) + let accumulate_constants (aconst,adiv) (v,offs,divi) = match v with + | None -> let gcdee = Z.gcd adiv divi in (Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi) + | Some (coeff,idx) -> let (somevar,someoffs,somedivi)=Rhs.subst (EConj.get_rhs d idx) idx (v,offs,divi) in (* normalize! *) + (Option.may (fun (coef,ter) -> expr.(ter) <- Q.(expr.(ter) + Q.make coef somedivi)) somevar; + let gcdee = Z.gcd adiv divi in (Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi)) in - let constant = List.fold_left accumulate_constants Z.zero monomiallist in (* abstract simplification of the guard wrt. reference variables *) - Some (Array.fold_lefti (fun list v (c) -> if Z.equal c Z.zero then list else (c,v)::list) [] expr, constant) ) + let constant = List.fold_left accumulate_constants (Z.zero,Z.one) monomiallist in (* abstract simplification of the guard wrt. reference variables *) + Some (Array.fold_lefti (fun list v (c) -> if Q.equal c Q.zero then list else (c.num,v,c.den)::list) [] expr, constant) ) let simplify_to_ref_and_offset (t: t) texp = BatOption.bind (simplified_monomials_from_texp t texp ) - (fun (sum_of_terms, constant) -> + (fun (sum_of_terms, (constant,divisor)) -> (match sum_of_terms with - | [] -> Some (None, constant) - | [(coeff,var)] when Z.equal coeff Z.one -> Some (Some var, constant) + | [] -> Some (None, constant,divisor) + | [(coeff,var,divi)] when Z.equal coeff Z.one -> Some (Rhs.canonicalize (Some (Z.mul divisor coeff,var), Z.mul constant divi,Z.mul divisor divi)) |_ -> None)) let simplify_to_ref_and_offset t texp = timing_wrap "coeff_vec" (simplify_to_ref_and_offset t) texp @@ -364,9 +364,12 @@ struct if t.d = None then None, None else match simplify_to_ref_and_offset t (Texpr1.to_expr texpr) with - | Some (None, offset) -> - (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string offset) (IntOps.BigIntOps.to_string offset); - Some offset, Some offset) + | Some (None, offset, divisor) when Z.equal (Z.rem offset divisor) Z.zero -> let res = Z.div offset divisor in + (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string res) (IntOps.BigIntOps.to_string res); + Some res, Some res) + | Some (None, offset, divisor) -> let res = Z.div offset divisor in + (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string res) (IntOps.BigIntOps.to_string (Z.add res Z.one)); + Some res, Some (Z.add res Z.one); failwith "ToDo: Rethink interval bounds (add or subtract depending on sign of res)") | _ -> None, None let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 From cad5f6e6c3b047cc30061188b57889b87a8b767a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 20 May 2024 13:03:12 +0300 Subject: [PATCH 104/566] Add BasePriv invariant_global tracing --- src/analyses/basePriv.ml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index af88cfb742..cbc11070d3 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1917,6 +1917,17 @@ struct if M.tracing then M.traceu "priv" "-> %a" BaseComponents.pretty r; r + let invariant_global ask getg g = + if M.tracing then M.traceli "priv" "invariant_global %a" V.pretty g; + let getg x = + let r = getg x in + if M.tracing then M.trace "priv" "getg %a -> %a" V.pretty x G.pretty r; + r + in + let r = invariant_global ask getg g in + if M.tracing then M.traceu "priv" "-> %a" Invariant.pretty r; + r + end let priv_module: (module S) Lazy.t = From 034492e12c4772f0d06af8f8de305b66703a0839 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Mon, 20 May 2024 23:32:21 +0200 Subject: [PATCH 105/566] [skip ci] replaced overspecialized subtract_with_constant by affine_transform --- .../apron/linearTwoVarEqualityDomain.apron.ml | 70 ++++++++----------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 1e60f28e8f..b296a3420d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -76,7 +76,7 @@ module EqualitiesConjunction = struct let nontrivial (_,econmap) lhs = IntMap.mem lhs econmap (** turn x = (cy+o)/d into y = (dx-o)/c*) - let reverse x (c,y,o,d) = (y,(Some (d,x),Z.neg o,c)) + let inverse x (c,y,o,d) = (y,(Some (d,x),Z.neg o,c)) (** sparse implementation of get rhs for lhs, but will default to no mapping for sparse entries *) let get_rhs (_,econmap) lhs = IntMap.find_default (Rhs.var_zero lhs) lhs econmap @@ -220,7 +220,7 @@ module EqualitiesConjunction = struct (* ts[x_j]=o2/d2 ========> ... *) | (None , o2, divi2) -> let newxi = Rhs.subst (None,o2,divi2) j (Some (coeff,j),offs,divi) in - let newxh1 = snd @@ reverse i (coeff1,h1,o1,divi1) in + let newxh1 = snd @@ inverse i (coeff1,h1,o1,divi1) in let newxh1 = Rhs.subst newxi i newxh1 in subst_var ts h1 newxh1 (* ts[x_j]=(c2*x_h2+o2)/d2 ========> ... *) @@ -230,16 +230,32 @@ module EqualitiesConjunction = struct (if not @@ Rhs.equal normalizedi oldi then raise Contradiction else ts) else if h1 < h2 (* good, we no unite the two equvalence classes; let's decide upon the representant *) then (* express h2 in terms of h1: *) - let (_,newh2)= reverse j (coeff2,h2,o2,divi2) in - let newh2 = Rhs.subst oldi i (Rhs.subst (snd @@ reverse i (coeff,j,offs,divi)) j newh2) in + let (_,newh2)= inverse j (coeff2,h2,o2,divi2) in + let newh2 = Rhs.subst oldi i (Rhs.subst (snd @@ inverse i (coeff,j,offs,divi)) j newh2) in subst_var ts h2 newh2 else (* express h1 in terms of h2: *) - let (_,newh1)= reverse i (coeff1,h1,o1,divi1) in + let (_,newh1)= inverse i (coeff1,h1,o1,divi1) in let newh1 = Rhs.subst normalizedj j (Rhs.subst (Some(coeff,j),offs,divi) i newh1) in subst_var ts h1 newh1)) in if M.tracing then M.trace "meet" "meet_with_one_conj conj: { %s } eq: var_%d=%s -> { %s } " (show (snd ts)) i (Rhs.show (var,offs,divi)) (show (snd ts)) ; res + (** affine transform variable i allover conj with transformer (Some (coeff,i)+offs)/divi *) + let affine_transform econ i (var,offs,divi) = + if nontrivial econ i then (** i cannot occur on any other rhs apart from itself *) + set_rhs econ i (Rhs.subst (get_rhs econ i) i (var,offs,divi)) + else (* var_i = var_i, i.e. it may occur on the rhs of other equalities *) + match var with + | None -> failwith "this is not a valid affine transformation" + | Some (coeff,j) -> + (* so now, we transform with the inverse of the transformer: *) + let inv = snd (inverse i (coeff,j,offs,divi)) in + IntMap.fold (fun k v acc -> + match v with + | (Some (c,x),o,d) when x=i-> set_rhs acc k (Rhs.subst inv i v) + | _ -> acc + ) (snd econ) econ + end (** [VarManagement] defines the type t of the affine equality domain (a record that contains an optional matrix and an apron environment) and provides the functions needed for handling variables (which are defined by [RelationDomain.D2]) such as [add_vars], [remove_vars]. @@ -325,33 +341,9 @@ struct let simplify_to_ref_and_offset t texp = timing_wrap "coeff_vec" (simplify_to_ref_and_offset t) texp (* Copy because function is not "with" so should not mutate inputs *) - let assign_const t var const = match t.d with - | None -> t - | Some t_d -> {d = Some (EConj.set_rhs t_d var (None, const,Z.one)); env = t.env} - - let subtract_const_from_var t var const = - match t.d with + let assign_const t var const divi = match t.d with | None -> t - | Some t_d -> - let subtract_const_from_var_for_single_equality index (eq_var_opt, off2) econ = - if index <> var then - begin match eq_var_opt with - | Some eq_var when eq_var = var -> - EConj.set_rhs econ index (eq_var_opt, Z.(off2 - const)) - | _ -> econ - end - else econ - in - let d = - if not @@ EConj.nontrivial t_d var - (* var is a reference variable -> it can appear on the right-hand side of an equality *) - then - (EConj.IntMap.fold (subtract_const_from_var_for_single_equality) (snd t_d) t_d) - else - (* var never appears on the right hand side-> we only need to modify the array entry at index var *) - EConj.set_rhs t_d var (Tuple2.map2 (Z.add const) (EConj.get_rhs t_d var)) - in - {d = Some d; env = t.env} + | Some t_d -> {d = Some (EConj.set_rhs t_d var (None, const, divi)); env = t.env} end @@ -580,15 +572,15 @@ struct | None -> (* Statement "assigned_var = ?" (non-linear assignment) *) forget_var t var - | Some (None, off) -> + | Some (None, off, divi) -> (* Statement "assigned_var = off" (constant assignment) *) - assign_const (forget_var t var) var_i off - | Some (Some exp_var, off) when var_i = exp_var -> - (* Statement "assigned_var = assigned_var + off" *) - subtract_const_from_var t var_i off - | Some (Some exp_var, off) -> - (* Statement "assigned_var = exp_var + off" (assigned_var is not the same as exp_var) *) - meet_with_one_conj (forget_var t var) var_i (Some (Z.one,exp_var), off, Z.one) + assign_const (forget_var t var) var_i off divi + | Some (Some (coeff_var,exp_var), off, divi) when var_i = exp_var -> + (* Statement "assigned_var = (coeff_var*assigned_var + off) / divi" *) + {d=Some (EConj.affine_transform d var_i (Some (coeff_var, var_i), off, divi)); env=t.env } + | Some (Some monomial, off, divi) -> + (* Statement "assigned_var = (monomial) + off / divi" (assigned_var is not the same as exp_var) *) + meet_with_one_conj (forget_var t var) var_i (Some (monomial), off, divi) end | None -> bot_env From 4d2668177c4bde52a89897253c2327806954b22c Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 21 May 2024 08:52:54 +0200 Subject: [PATCH 106/566] [skip ci] bounds --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index b296a3420d..a0bbcbce39 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -360,8 +360,12 @@ struct (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string res) (IntOps.BigIntOps.to_string res); Some res, Some res) | Some (None, offset, divisor) -> let res = Z.div offset divisor in - (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string res) (IntOps.BigIntOps.to_string (Z.add res Z.one)); - Some res, Some (Z.add res Z.one); failwith "ToDo: Rethink interval bounds (add or subtract depending on sign of res)") + let (lower,upper) = if Z.lt res Z.zero then + (Z.sub res Z.one,res) + else + (res,Z.add res Z.one) in + (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string lower) (IntOps.BigIntOps.to_string upper); + Some lower, Some upper) | _ -> None, None let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 From fa58e2e301432c85c89b596d1c87b0923816c833 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 21 May 2024 09:24:25 +0200 Subject: [PATCH 107/566] [skip ci] leq/implies now also for coefficients --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index a0bbcbce39..5234f8d2fa 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -451,11 +451,13 @@ struct let leq t1 t2 = let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) - let implies ts i (var, b) = - let tuple_cmp = Tuple2.eq (Option.eq ~eq:Int.equal) (Z.equal) in + let implies ts i (var, offs, divi) = + let tuple_cmp = Tuple3.eq (Option.eq ~eq:(Tuple2.eq (Z.equal) (Int.equal))) (Z.equal) (Z.equal) in match var with - | None -> tuple_cmp (var, b) (EConj.get_rhs ts i) - | Some j -> tuple_cmp (EConj.get_rhs ts i) @@ Tuple2.map2 (Z.add b) (EConj.get_rhs ts j) + (* directly compare in case of constant value *) + | None -> tuple_cmp (var, offs, divi) (EConj.get_rhs ts i) + (* normalize in case of a full blown equality *) + | Some (coeffj,j) -> tuple_cmp (EConj.get_rhs ts i) @@ Rhs.subst (EConj.get_rhs ts j) j (var, offs, divi) in if env_comp = -2 || env_comp > 0 then false else if is_bot_env t1 || is_top t2 then true else From ba485801970de0bf25618b8516b8ef4a89549523 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 21 May 2024 10:42:15 +0200 Subject: [PATCH 108/566] [skip ci] migrated meet_tcons --- .../apron/linearTwoVarEqualityDomain.apron.ml | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 5234f8d2fa..51c6d702ef 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -690,9 +690,9 @@ struct | Some d -> match simplified_monomials_from_texp t (Texpr1.to_expr @@ Tcons1.get_texpr1 tcons) with | None -> t - | Some (sum_of_terms, constant) ->( + | Some (sum_of_terms, (constant,divisor)) ->( match sum_of_terms with - | [] -> (* no reference variables in the guard *) + | [] -> (* no reference variables in the guard, so check constant for zero *) begin match Tcons1.get_typ tcons with | EQ when Z.equal constant Z.zero -> t | SUPEQ when Z.geq constant Z.zero -> t @@ -701,15 +701,20 @@ struct | EQMOD _ -> t | _ -> bot_env (* all other results are violating the guard *) end - | [(varexpr, index)] -> (* guard has a single reference variable only *) - if Tcons1.get_typ tcons = EQ && Z.divisible constant varexpr then - meet_with_one_conj t index (None, (Z.(-(constant) / varexpr)),Z.one) + | [(coeff, index, divi)] -> (* guard has a single reference variable only *) + if Tcons1.get_typ tcons = EQ then + meet_with_one_conj t index (Rhs.canonicalize (None, Z.(divi*constant),Z.(coeff*divisor))) else t (* only EQ is supported in equality based domains *) - | [(a1,var1); (a2,var2)] -> (* two variables in relation needs a little sorting out *) + | [(c1,var1,d1); (c2,var2,d2)] -> (* two variables in relation needs a little sorting out *) begin match Tcons1.get_typ tcons with - | EQ when Z.(a1 * a2 = -one) -> (* var1-var1 or var2-var1 *) - meet_with_one_conj t var2 (Some (Z.one,var1), Z.mul a1 constant,Z.one) + | EQ -> (* c1*var1/d1 + c2*var2/d2 +constant/divisor = 0*) + (* ======> c1*divisor*d2 * var1 = -c2*divisor*d1 * var2 +constant*-d1*d2*) + (* \/ c2*divisor*d1 * var2 = -c1*divisor*d2 * var1 +constant*-d1*d2*) + if var1 < var2 then + meet_with_one_conj t var2 (Rhs.canonicalize (Some (Z.neg @@ Z.(c1*divisor),var1),Z.neg @@ Z.(constant*d2*d1),Z.(c2*divisor*d1))) + else + meet_with_one_conj t var1 (Rhs.canonicalize (Some (Z.neg @@ Z.(c2*divisor),var2),Z.neg @@ Z.(constant*d2*d1),Z.(c1*divisor*d2))) | _-> t (* Not supported in equality based 2vars without coeffiients *) end | _ -> t (* For equalities of more then 2 vars we just return t *)) From bd329e16f72f296bdf88f46d31fcfc4477db3ffc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 21 May 2024 11:53:44 +0300 Subject: [PATCH 109/566] Fix mutex-meet invariant_global not including MUTEX_INITS --- src/analyses/apron/relationPriv.apron.ml | 4 ++-- src/analyses/basePriv.ml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index cb71884c8c..5ffb96650c 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -694,14 +694,14 @@ struct let finalize () = () let invariant_global (ask: Q.ask) (getg: V.t -> G.t): V.t -> Invariant.t = function - | `Left m' as m -> (* mutex *) + | `Left m' -> (* mutex *) let atomic = LockDomain.Addr.equal m' (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) in if atomic || ask.f (GhostVarAvailable (Locked m')) then ( (* filters like query_invariant *) let one_var = GobConfig.get_bool "ana.relation.invariant.one-var" in let exact = GobConfig.get_bool "witness.invariant.exact" in - let rel = keep_only_protected_globals ask m' (getg m) in + let rel = keep_only_protected_globals ask m' (get_m_with_mutex_inits ask getg m') in (* TODO: disjunct with mutex_inits instead of join? *) let inv = RD.invariant rel |> List.enum diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index cbc11070d3..c244e7f3bf 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -250,7 +250,7 @@ struct let invariant_global ask getg = function | `Right g' -> (* global *) - ValueDomain.invariant_global (read_unprotected_global getg) g' + ValueDomain.invariant_global (read_unprotected_global getg) g' (* TODO: disjunct with mutex_inits instead of join? *) | _ -> (* mutex *) Invariant.none @@ -332,10 +332,10 @@ struct include PerMutexPrivBase let invariant_global (ask: Q.ask) getg = function - | `Left m' as m -> (* mutex *) + | `Left m' -> (* mutex *) let atomic = LockDomain.Addr.equal m' (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) in if atomic || ask.f (GhostVarAvailable (Locked m')) then ( - let cpa = getg m in + let cpa = get_m_with_mutex_inits ask getg m' in (* TODO: disjunct with mutex_inits instead of join? *) let inv = CPA.fold (fun v _ acc -> if ask.f (MustBeProtectedBy {mutex = m'; global = v; write = true; protection = Strong}) then let inv = ValueDomain.invariant_global (fun g -> CPA.find g cpa) v in @@ -688,7 +688,7 @@ struct let invariant_global ask getg = function | `Middle g -> (* global *) - ValueDomain.invariant_global (read_unprotected_global getg) g + ValueDomain.invariant_global (read_unprotected_global getg) g (* TODO: disjunct with mutex_inits instead of join? *) | `Left _ | `Right _ -> (* mutex or thread *) Invariant.none From 80ef4bd311de6fb433683c042de56706e8cc7db8 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 21 May 2024 17:51:05 +0200 Subject: [PATCH 110/566] [skip ci] formulated new join sorting criterium --- .../apron/linearTwoVarEqualityDomain.apron.ml | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 51c6d702ef..8e6a97d18c 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -478,32 +478,36 @@ struct (* joinfunction handles the dirty details of performing an "inner join" on the lhs of both bindings; in the resulting binding, the lhs is then mapped to values that are later relevant for sorting/grouping, i.e. - lhs itself - - the difference of both offsets + - the affine transformation from refvar1 to refvar2, i.E. the parameters A and B of the general affine transformer Ax+B - rhs1 - rhs2 however, we have to account for the sparseity of EConj maps by manually patching holes with default values *) - let joinfunction lhs rhs1 rhs2 = match rhs1, rhs2 with - | Some (ai,aj),Some (bi,bj) -> Some (lhs,Z.(aj - bj),(ai,aj),(bi,bj)) (* this is explicitely what we want *) - | None, Some (bi,bj) -> Some (lhs,Z.neg bj ,Rhs.var_zero lhs,(bi,bj)) (* account for the sparseity of binding 1 *) - | Some (ai,aj), None -> Some (lhs,aj ,(ai,aj),Rhs.var_zero lhs) (* account for the sparseity of binding 2 *) - | _,_ -> None (* no binding for lhs in both maps is replicated implicitely in a sparse result map *) + let joinfunction lhs rhs1 rhs2 = let coeff = Option.map_default fst Z.one in match rhs1, rhs2 with + (* Compute Ax+B such that (coeff1*(Ax+B)+off1)/d1 = (coeff2*x+off2)/d2 *) + (* ====> A = (coeff2*d1)/(coeff1*d2) /\ B = (off2*d1-off1*d2)/(c1*c2) *) + (* lhs A B rhs1 rhs2 *) + (*TODO*: if this works, make it more concise *) + | Some (ai,aj,ak), Some (bi,bj,bk) -> Some (lhs,Q.make Z.(ak*coeff bi) Z.(bk* coeff ai),Q.make Z.(bj*ak-aj*bk) Z.(bk*coeff ai), (ai,aj,ak) , (bi,bj,bk)) (* this is explicitely what we want *) + | None , Some (bi,bj,bk) -> Some (lhs,Q.make Z.( coeff bi) Z.(bk ),Q.make Z.(bj ) Z.(bk ), Rhs.var_zero lhs, (bi,bj,bk)) (* account for the sparseity of binding 1 *) + | Some (ai,aj,ak), None -> Some (lhs,Q.make Z.(ak ) Z.( coeff ai),Q.make Z.( neg aj) Z.( coeff ai), (ai,aj,ak), Rhs.var_zero lhs) (* account for the sparseity of binding 2 *) + | _,_ -> None (* no binding for lhs in both maps is replicated implicitely in a sparse result map *) in let table = List.of_enum @@ EConj.IntMap.values @@ EConj.IntMap.merge joinfunction (snd ad) (snd bd) in - (*compare two variables for grouping depending on delta function and reference index*) - let cmp_z (_, t0i, t1i, t2i) (_, t0j, t1j, t2j) = - let cmp_ref = Option.compare ~cmp:Int.compare in - Tuple3.compare ~cmp1:cmp_ref ~cmp2:cmp_ref ~cmp3:Z.compare (fst t1i, fst t2i, t0i) (fst t1j, fst t2j, t0j) + (* compare two variables for grouping depending on affine function parameters a, b and reference variable indices *) + let cmp_z (_, ai, bi, t1i, t2i) (_, aj, bj, t1j, t2j) = + let cmp_ref = Option.compare ~cmp:(fun x y -> Int.compare (snd x) (snd y)) in + Tuple4.compare ~cmp1:cmp_ref ~cmp2:cmp_ref ~cmp3:Q.compare ~cmp4:Q.compare (Tuple3.first t1i, Tuple3.first t2i, ai, bi) (Tuple3.first t1j, Tuple3.first t2j, aj, bj) in - (*Calculate new components as groups*) + (* Calculate new components as groups *) let new_components = BatList.group cmp_z table in - (*Adjust the domain array to represent the new components*) - let modify map idx_h b_h (idx, _, (opt1, z1), (opt2, z2)) = EConj.set_rhs map (idx) - (if opt1 = opt2 && Z.equal z1 z2 then (opt1, z1) - else (Some idx_h, Z.(z1 - b_h))) + (* Adjust the domain map to represent the new components *) + let modify map idx_h offs_h (idx, _, _, (opt1, z1, d1), (opt2, z2, d2)) = EConj.set_rhs map (idx) + (if opt1 = opt2 && Z.equal z1 z2 && Z.equal d1 d2 then (opt1, z1, d1) + else (Some idx_h, Z.(z1 - offs_h))) in let iterate map l = match l with - | (idx_h, _, (_, b_h), _) :: t -> List.fold (fun map' e -> modify map' idx_h b_h e) map l + | (idx_h, _, _, (_, offs_h, divi_h), _) :: t -> List.fold (fun map' e -> modify map' idx_h offs_h e) map l | [] -> let exception EmptyComponent in raise EmptyComponent in Some (List.fold iterate (EConj.make_empty_conj @@ fst ad) new_components) From 7e5b727373e27d3cd0565ccb9f44fc9936934197 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 21 May 2024 18:03:23 +0200 Subject: [PATCH 111/566] [skip ci] satisfy semgrep --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 8e6a97d18c..6c12744228 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -361,9 +361,9 @@ struct Some res, Some res) | Some (None, offset, divisor) -> let res = Z.div offset divisor in let (lower,upper) = if Z.lt res Z.zero then - (Z.sub res Z.one,res) + (Z.pred res,res) else - (res,Z.add res Z.one) in + (res,Z.succ res) in (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string lower) (IntOps.BigIntOps.to_string upper); Some lower, Some upper) | _ -> None, None From ad01be4ebe8012ee02bd1537386dbf00bd3cad1c Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 22 May 2024 10:27:40 +0200 Subject: [PATCH 112/566] [skip ci] finally affine transform the reference variables in join --- .../apron/linearTwoVarEqualityDomain.apron.ml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 6c12744228..cff8cab293 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -501,13 +501,16 @@ struct (* Calculate new components as groups *) let new_components = BatList.group cmp_z table in (* Adjust the domain map to represent the new components *) - let modify map idx_h offs_h (idx, _, _, (opt1, z1, d1), (opt2, z2, d2)) = EConj.set_rhs map (idx) - (if opt1 = opt2 && Z.equal z1 z2 && Z.equal d1 d2 then (opt1, z1, d1) - else (Some idx_h, Z.(z1 - offs_h))) + let modify map x (refmonom, offs, divi) (idx, _, _, (monom1, z1, d1), (monom2, z2, d2)) = EConj.set_rhs map (idx) + (if monom1 = monom2 && Z.equal z1 z2 && Z.equal d1 d2 then (monom1, z1, d1) + else + let refcoeff = Option.map_default fst Z.one refmonom in + let coeff1 = Option.map_default fst Z.one monom1 in + (Some (Z.(coeff1*divi),x), Z.((z1*refcoeff)-(offs*coeff1)), Z.(refcoeff*d1)) ) in let iterate map l = match l with - | (idx_h, _, _, (_, offs_h, divi_h), _) :: t -> List.fold (fun map' e -> modify map' idx_h offs_h e) map l + | (idx_h, _, _, rhs_h, _) :: t -> List.fold (fun acc e -> modify acc idx_h rhs_h e) map l | [] -> let exception EmptyComponent in raise EmptyComponent in Some (List.fold iterate (EConj.make_empty_conj @@ fst ad) new_components) From 9923eafdc167b0453cfad0cff3d4fffdc5617c0c Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 22 May 2024 11:28:29 +0200 Subject: [PATCH 113/566] cosmetics --- .../apron/linearTwoVarEqualityDomain.apron.ml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index cff8cab293..6346dc0975 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -25,7 +25,8 @@ module Rhs = struct let var_zero i = (Some (Z.one,i), Z.zero, Z.one) let show_coeff c = if Z.equal c Z.one then "" - else (Z.to_string c) ^"*" + else if Z.equal c Z.minus_one then "-" + else (Z.to_string c) ^"·" let show_rhs_formatted formatter = function | (Some (coeff,v), o,_) when Z.equal o Z.zero -> Printf.sprintf "%s%s" (show_coeff coeff) (formatter v) | (Some (coeff,v), o,_) -> Printf.sprintf "%s%s%+Ld" (show_coeff coeff) (formatter v) (Z.to_int64 o) @@ -404,7 +405,13 @@ struct (** prints the current variable equalities with resolved variable names *) let show varM = - let lookup i = Var.to_string (Environment.var_of_dim varM.env i) in + let lookup i = + let transl = [|"₀";"₁";"₂";"₃";"₄";"₅";"₆";"₇";"₈";"₉"|] in + let res = Var.to_string (Environment.var_of_dim varM.env i) in + match String.split_on_char '#' res with + | varname::rest::[] -> varname ^ (try String.fold_left (fun acc c -> acc ^ transl.(Char.code c - Char.code '0')) "" rest with _ -> "#"^rest) + | _ -> failwith "Variable name not found" + in match varM.d with | None -> "⊥\n" | Some arr when EConj.is_top_con arr -> "⊤\n" From dfeb71537c731b2c839afe04df1379f799f74fb5 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 22 May 2024 14:59:16 +0200 Subject: [PATCH 114/566] sign errors corrected --- .../apron/linearTwoVarEqualityDomain.apron.ml | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 6346dc0975..d51bebb332 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -41,7 +41,8 @@ module Rhs = struct let gcd = match v with | Some (c,_) -> Z.gcd c gcd | None -> gcd - in (BatOption.map (fun (coeff,i) -> (Z.div coeff gcd,i)) v,Z.div o gcd, Z.div d gcd) + in let gcd = if (Z.(lt d Z.zero)) then Z.neg gcd else gcd in + (BatOption.map (fun (coeff,i) -> (Z.div coeff gcd,i)) v,Z.div o gcd, Z.div d gcd) (** Substitute rhs for varx in rhs' *) let subst rhs varx rhs' = @@ -238,7 +239,7 @@ module EqualitiesConjunction = struct let (_,newh1)= inverse i (coeff1,h1,o1,divi1) in let newh1 = Rhs.subst normalizedj j (Rhs.subst (Some(coeff,j),offs,divi) i newh1) in subst_var ts h1 newh1)) in - if M.tracing then M.trace "meet" "meet_with_one_conj conj: { %s } eq: var_%d=%s -> { %s } " (show (snd ts)) i (Rhs.show (var,offs,divi)) (show (snd ts)) + if M.tracing then M.trace "meet_with_one_conj" "meet_with_one_conj conj: %s eq: var_%d=%s -> %s " (show (snd ts)) i (Rhs.show (var,offs,divi)) (show (snd ts)) ; res (** affine transform variable i allover conj with transformer (Some (coeff,i)+offs)/divi *) @@ -331,6 +332,12 @@ struct let constant = List.fold_left accumulate_constants (Z.zero,Z.one) monomiallist in (* abstract simplification of the guard wrt. reference variables *) Some (Array.fold_lefti (fun list v (c) -> if Q.equal c Q.zero then list else (c.num,v,c.den)::list) [] expr, constant) ) + let simplified_monomials_from_texp (t: t) texp = + let res = simplified_monomials_from_texp t texp in + if M.tracing then M.tracel "from_texp" "%s %s -> %s" (EConj.show @@ snd @@ BatOption.get t.d) (Format.asprintf "%a" Texpr1.print_expr texp) + (BatOption.map_default (fun (l,(o,d)) -> List.fold_right (fun (a,x,b) acc -> Printf.sprintf "%s*var_%d/%s + %s" (Z.to_string a) x (Z.to_string b) acc) l ((Z.to_string o)^"/"^(Z.to_string d))) "" res); + res + let simplify_to_ref_and_offset (t: t) texp = BatOption.bind (simplified_monomials_from_texp t texp ) (fun (sum_of_terms, (constant,divisor)) -> @@ -403,15 +410,22 @@ struct (** is_top returns true for top_of array and empty array; precondition: t.env and t.d are of same size *) let is_top t = Environment.equal empty_env t.env && GobOption.exists EConj.is_top_con t.d + let to_subscript i = + let transl = [|"₀";"₁";"₂";"₃";"₄";"₅";"₆";"₇";"₈";"₉"|] in + let rec subscr i = + if i = 0 then "" + else (subscr (i/10)) ^ transl.(i mod 10) in + subscr i + + let show_var env i = + let transl = [|"₀";"₁";"₂";"₃";"₄";"₅";"₆";"₇";"₈";"₉"|] in + let res = Var.to_string (Environment.var_of_dim env i) in + match String.split_on_char '#' res with + | varname::rest::[] -> varname ^ (try String.fold_left (fun acc c -> acc ^ transl.(Char.code c - Char.code '0')) "" rest with _ -> "#"^rest) + | _ -> failwith "Variable name not found" + (** prints the current variable equalities with resolved variable names *) let show varM = - let lookup i = - let transl = [|"₀";"₁";"₂";"₃";"₄";"₅";"₆";"₇";"₈";"₉"|] in - let res = Var.to_string (Environment.var_of_dim varM.env i) in - match String.split_on_char '#' res with - | varname::rest::[] -> varname ^ (try String.fold_left (fun acc c -> acc ^ transl.(Char.code c - Char.code '0')) "" rest with _ -> "#"^rest) - | _ -> failwith "Variable name not found" - in match varM.d with | None -> "⊥\n" | Some arr when EConj.is_top_con arr -> "⊤\n" @@ -419,7 +433,7 @@ struct if is_bot varM then "Bot \n" else - EConj.show_formatted lookup (snd arr) ^ (" with dimension " ^ (string_of_int @@ fst arr)) + EConj.show_formatted (show_var varM.env) (snd arr) ^ (to_subscript @@ fst arr) let pretty () (x:t) = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n\nequalities\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) @@ -437,7 +451,7 @@ struct let meet_with_one_conj t i e = let res = meet_with_one_conj t i e in - if M.tracing then M.trace "meet" "meet_with_one_conj %s with var_%d=%s -> %s" (show t) i (Rhs.show e) (show res); + if M.tracing then M.trace "meet" "%s with single eq %s=%s -> %s" (show t) (show_var t.env i) (Rhs.show_rhs_formatted (show_var t.env) e) (show res); res let meet t1 t2 = @@ -517,7 +531,7 @@ struct in let iterate map l = match l with - | (idx_h, _, _, rhs_h, _) :: t -> List.fold (fun acc e -> modify acc idx_h rhs_h e) map l + | (idx_h, _, _, rhs_h, _) :: t -> List.fold (fun acc e -> modify acc idx_h rhs_h e) map l | [] -> let exception EmptyComponent in raise EmptyComponent in Some (List.fold iterate (EConj.make_empty_conj @@ fst ad) new_components) @@ -717,7 +731,7 @@ struct end | [(coeff, index, divi)] -> (* guard has a single reference variable only *) if Tcons1.get_typ tcons = EQ then - meet_with_one_conj t index (Rhs.canonicalize (None, Z.(divi*constant),Z.(coeff*divisor))) + meet_with_one_conj t index (Rhs.canonicalize (None, Z.neg @@ Z.(divi*constant),Z.(coeff*divisor))) else t (* only EQ is supported in equality based domains *) | [(c1,var1,d1); (c2,var2,d2)] -> (* two variables in relation needs a little sorting out *) From c6928c58a426674077bd38a5a02149edefc89745 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 22 May 2024 15:10:02 +0200 Subject: [PATCH 115/566] do not enforce pattern on variable names --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index d51bebb332..60e8f50d9d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -422,7 +422,7 @@ struct let res = Var.to_string (Environment.var_of_dim env i) in match String.split_on_char '#' res with | varname::rest::[] -> varname ^ (try String.fold_left (fun acc c -> acc ^ transl.(Char.code c - Char.code '0')) "" rest with _ -> "#"^rest) - | _ -> failwith "Variable name not found" + | _ -> res (** prints the current variable equalities with resolved variable names *) let show varM = From ada7065af6bd9c276a0de59e96822bdf0ff4e504 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Wed, 22 May 2024 22:44:09 +0200 Subject: [PATCH 116/566] migration to appendix A - join (part 1) --- .../apron/linearTwoVarEqualityDomain.apron.ml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 60e8f50d9d..8f9694de2c 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -503,7 +503,21 @@ struct - rhs1 - rhs2 however, we have to account for the sparseity of EConj maps by manually patching holes with default values *) - let joinfunction lhs rhs1 rhs2 = let coeff = Option.map_default fst Z.one in match rhs1, rhs2 with + let joinfunction lhs rhs1 rhs2 = + let pairing = match rhs1,rhs2 with (* first of all re-instantiate implicit sparse elements *) + | Some a, Some b -> Some (a,b) + | None, Some b -> Some (Rhs.var_zero lhs,b) + | Some a, None -> Some (a,Rhs.var_zero lhs) + | _ -> None + in let _= BatOption.map (function + | (Some (c1,var1),o1,d1) as r1 ,(Some (c2,var2),o2,d2) as r2 -> lhs, Q.one, Q.one, r1, r2 + | (None, o1,d1) as r1, (Some (c2,var2),o2,d2) as r2 -> lhs, Q.one, Q.one, r1, r2 + | (Some (c1,var1),o1,d1) as r1, (None ,o2,d2) as r2 -> lhs, Q.one, Q.one, r1, r2 + | (None, o1,d1) as r1, (None ,o2,d2) as r2 -> + let magicnumber=Z.((o1/d1)-(o2/d2)) in + lhs, Q.make Z.(o1/magicnumber) Z.one,Q.make Z.(o1 mod magicnumber) Z.one,r1, r2 + ) pairing in () ; + let coeff = Option.map_default fst Z.one in match rhs1, rhs2 with (* Compute Ax+B such that (coeff1*(Ax+B)+off1)/d1 = (coeff2*x+off2)/d2 *) (* ====> A = (coeff2*d1)/(coeff1*d2) /\ B = (off2*d1-off1*d2)/(c1*c2) *) (* lhs A B rhs1 rhs2 *) From 366b04c2e2244276fde4963089da372c26439451 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 22 May 2024 15:39:15 +0200 Subject: [PATCH 117/566] error in tracing --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 8f9694de2c..e953a420a4 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -239,7 +239,7 @@ module EqualitiesConjunction = struct let (_,newh1)= inverse i (coeff1,h1,o1,divi1) in let newh1 = Rhs.subst normalizedj j (Rhs.subst (Some(coeff,j),offs,divi) i newh1) in subst_var ts h1 newh1)) in - if M.tracing then M.trace "meet_with_one_conj" "meet_with_one_conj conj: %s eq: var_%d=%s -> %s " (show (snd ts)) i (Rhs.show (var,offs,divi)) (show (snd ts)) + if M.tracing then M.trace "meet_with_one_conj" "meet_with_one_conj conj: %s eq: var_%d=%s -> %s " (show (snd ts)) i (Rhs.show (var,offs,divi)) (show (snd res)) ; res (** affine transform variable i allover conj with transformer (Some (coeff,i)+offs)/divi *) @@ -451,7 +451,7 @@ struct let meet_with_one_conj t i e = let res = meet_with_one_conj t i e in - if M.tracing then M.trace "meet" "%s with single eq %s=%s -> %s" (show t) (show_var t.env i) (Rhs.show_rhs_formatted (show_var t.env) e) (show res); + if M.tracing then M.tracel "meet" "%s with single eq %s=%s -> %s" (show t) (Z.(to_string @@ Tuple3.third e)^ show_var t.env i) (Rhs.show_rhs_formatted (show_var t.env) e) (show res); res let meet t1 t2 = From cb3b486b680ceff271afc0afda68a9b639bfb01d Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Thu, 23 May 2024 09:19:21 +0200 Subject: [PATCH 118/566] join of two different constants still remains in one equivalence class --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e953a420a4..a5d27fec39 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -343,7 +343,7 @@ struct (fun (sum_of_terms, (constant,divisor)) -> (match sum_of_terms with | [] -> Some (None, constant,divisor) - | [(coeff,var,divi)] when Z.equal coeff Z.one -> Some (Rhs.canonicalize (Some (Z.mul divisor coeff,var), Z.mul constant divi,Z.mul divisor divi)) + | [(coeff,var,divi)] -> Some (Rhs.canonicalize (Some (Z.mul divisor coeff,var), Z.mul constant divi,Z.mul divisor divi)) |_ -> None)) let simplify_to_ref_and_offset t texp = timing_wrap "coeff_vec" (simplify_to_ref_and_offset t) texp @@ -514,8 +514,7 @@ struct | (None, o1,d1) as r1, (Some (c2,var2),o2,d2) as r2 -> lhs, Q.one, Q.one, r1, r2 | (Some (c1,var1),o1,d1) as r1, (None ,o2,d2) as r2 -> lhs, Q.one, Q.one, r1, r2 | (None, o1,d1) as r1, (None ,o2,d2) as r2 -> - let magicnumber=Z.((o1/d1)-(o2/d2)) in - lhs, Q.make Z.(o1/magicnumber) Z.one,Q.make Z.(o1 mod magicnumber) Z.one,r1, r2 + lhs, Q.make Z.((o1*d2)-(o2*d1)) Z.(d1*d2), Q.zero, r1, r2 ) pairing in () ; let coeff = Option.map_default fst Z.one in match rhs1, rhs2 with (* Compute Ax+B such that (coeff1*(Ax+B)+off1)/d1 = (coeff2*x+off2)/d2 *) @@ -543,9 +542,9 @@ struct let coeff1 = Option.map_default fst Z.one monom1 in (Some (Z.(coeff1*divi),x), Z.((z1*refcoeff)-(offs*coeff1)), Z.(refcoeff*d1)) ) in - let iterate map l = + let iterate map l = match l with - | (idx_h, _, _, rhs_h, _) :: t -> List.fold (fun acc e -> modify acc idx_h rhs_h e) map l + | (idx_h, a, b, rhs_h, _) :: t -> M.trace "join" " join me in dept %s %s" (Q.to_string a) (Q.to_string b); List.fold (fun acc e -> modify acc idx_h rhs_h e) map l | [] -> let exception EmptyComponent in raise EmptyComponent in Some (List.fold iterate (EConj.make_empty_conj @@ fst ad) new_components) From 58c4e7dc08079b10fbe553b69e359485dfc4f930 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Thu, 23 May 2024 10:33:52 +0200 Subject: [PATCH 119/566] made join criteria much more legible --- .../apron/linearTwoVarEqualityDomain.apron.ml | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index a5d27fec39..ff4d19414f 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -499,32 +499,23 @@ struct (* joinfunction handles the dirty details of performing an "inner join" on the lhs of both bindings; in the resulting binding, the lhs is then mapped to values that are later relevant for sorting/grouping, i.e. - lhs itself - - the affine transformation from refvar1 to refvar2, i.E. the parameters A and B of the general affine transformer Ax+B + - criteria A and B that characterize equivalence class, depending on the reference variable and the affine expression parameters wrt. each EConj - rhs1 - rhs2 however, we have to account for the sparseity of EConj maps by manually patching holes with default values *) let joinfunction lhs rhs1 rhs2 = - let pairing = match rhs1,rhs2 with (* first of all re-instantiate implicit sparse elements *) - | Some a, Some b -> Some (a,b) - | None, Some b -> Some (Rhs.var_zero lhs,b) - | Some a, None -> Some (a,Rhs.var_zero lhs) - | _ -> None - in let _= BatOption.map (function - | (Some (c1,var1),o1,d1) as r1 ,(Some (c2,var2),o2,d2) as r2 -> lhs, Q.one, Q.one, r1, r2 - | (None, o1,d1) as r1, (Some (c2,var2),o2,d2) as r2 -> lhs, Q.one, Q.one, r1, r2 - | (Some (c1,var1),o1,d1) as r1, (None ,o2,d2) as r2 -> lhs, Q.one, Q.one, r1, r2 - | (None, o1,d1) as r1, (None ,o2,d2) as r2 -> - lhs, Q.make Z.((o1*d2)-(o2*d1)) Z.(d1*d2), Q.zero, r1, r2 - ) pairing in () ; - let coeff = Option.map_default fst Z.one in match rhs1, rhs2 with - (* Compute Ax+B such that (coeff1*(Ax+B)+off1)/d1 = (coeff2*x+off2)/d2 *) - (* ====> A = (coeff2*d1)/(coeff1*d2) /\ B = (off2*d1-off1*d2)/(c1*c2) *) - (* lhs A B rhs1 rhs2 *) - (*TODO*: if this works, make it more concise *) - | Some (ai,aj,ak), Some (bi,bj,bk) -> Some (lhs,Q.make Z.(ak*coeff bi) Z.(bk* coeff ai),Q.make Z.(bj*ak-aj*bk) Z.(bk*coeff ai), (ai,aj,ak) , (bi,bj,bk)) (* this is explicitely what we want *) - | None , Some (bi,bj,bk) -> Some (lhs,Q.make Z.( coeff bi) Z.(bk ),Q.make Z.(bj ) Z.(bk ), Rhs.var_zero lhs, (bi,bj,bk)) (* account for the sparseity of binding 1 *) - | Some (ai,aj,ak), None -> Some (lhs,Q.make Z.(ak ) Z.( coeff ai),Q.make Z.( neg aj) Z.( coeff ai), (ai,aj,ak), Rhs.var_zero lhs) (* account for the sparseity of binding 2 *) - | _,_ -> None (* no binding for lhs in both maps is replicated implicitely in a sparse result map *) + (match rhs1,rhs2 with (* first of all re-instantiate implicit sparse elements *) + | Some a, Some b -> Some (a,b) + | None, Some b -> Some (Rhs.var_zero lhs,b) + | Some a, None -> Some (a,Rhs.var_zero lhs) + | _ -> None) + |> + BatOption.map (fun (r1,r2) -> match (r1,r2) with (* criterion A , criterion B *) + | (Some (c1,_),o1,d1), (Some (c2,_),o2,d2)-> lhs, Q.make Z.((o1*d2)-(o2*d1)) Z.(c1*d2), Q.make Z.(c2*d2) Z.(c1*d1), r1, r2 + | (None, oc,dc), (Some (cv,_),ov,dv) + | (Some (cv,_),ov,dv), (None ,oc,dc)-> lhs, Q.make Z.((oc*dv)-(ov*dc)) Z.(dc*cv), Q.one , r1, r2 (* equivalence class defined by (oc/dc-ov/dv)/(cv/dv) *) + | (None, o1,d1), (None ,o2,d2)-> lhs, (if Z.(zero = ((o1*d2)-(o2*d1))) then Q.one else Q.zero), Q.zero, r1, r2 (* only two equivalence classes: constants with matching values or constants with different values *) + ) in let table = List.of_enum @@ EConj.IntMap.values @@ EConj.IntMap.merge joinfunction (snd ad) (snd bd) in (* compare two variables for grouping depending on affine function parameters a, b and reference variable indices *) @@ -544,7 +535,7 @@ struct in let iterate map l = match l with - | (idx_h, a, b, rhs_h, _) :: t -> M.trace "join" " join me in dept %s %s" (Q.to_string a) (Q.to_string b); List.fold (fun acc e -> modify acc idx_h rhs_h e) map l + | (idx_h, a, b, rhs_h, _) :: t -> M.trace "join" " iterate through equivalence group with a=%s and b=%s" (Q.to_string a) (Q.to_string b); List.fold (fun acc e -> modify acc idx_h rhs_h e) map l | [] -> let exception EmptyComponent in raise EmptyComponent in Some (List.fold iterate (EConj.make_empty_conj @@ fst ad) new_components) From b5fed0874204e5f12e90eceaa23601653c35dae8 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Thu, 23 May 2024 16:32:21 +0200 Subject: [PATCH 120/566] added case distinction for equivalence classes --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index ff4d19414f..e49b3a0f93 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -535,7 +535,11 @@ struct in let iterate map l = match l with - | (idx_h, a, b, rhs_h, _) :: t -> M.trace "join" " iterate through equivalence group with a=%s and b=%s" (Q.to_string a) (Q.to_string b); List.fold (fun acc e -> modify acc idx_h rhs_h e) map l + | (x, a, b, ((Some _,_,_) as rhs), ((Some _,_,_) as rhs')) :: t -> M.trace "join" "handle var-var equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l + | (x, a, b, ((Some _,_,_) as rhs), ((None,_,_) as rhs')) :: t -> M.trace "join" "handle var-const equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l + | (x, a, b, ((None,_,_) as rhs), ((Some _,_,_) as rhs')) :: t -> M.trace "join" "handle const-var equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l + | (x, a, b, ((None,o1,d1) as rhs), ((None,o2,d2) )) :: t when (o1,d1)=(o2,d2) -> M.trace "join" "handle const-const equivalence group" ; List.fold (fun acc e -> modify acc x rhs e) map l + | (x, a, b, ((None,_,_) as rhs), ((None,_,_) as rhs')) :: t -> M.trace "join" "handle const1-const2 equivalence group" ; List.fold (fun acc e -> modify acc x rhs e) map l | [] -> let exception EmptyComponent in raise EmptyComponent in Some (List.fold iterate (EConj.make_empty_conj @@ fst ad) new_components) From 083d1ea88a95cf0277ae8cbfeff07c658ff4b83c Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Thu, 23 May 2024 20:13:37 +0200 Subject: [PATCH 121/566] satisfy semgrep --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index e49b3a0f93..89cead570b 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -535,11 +535,11 @@ struct in let iterate map l = match l with - | (x, a, b, ((Some _,_,_) as rhs), ((Some _,_,_) as rhs')) :: t -> M.trace "join" "handle var-var equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l - | (x, a, b, ((Some _,_,_) as rhs), ((None,_,_) as rhs')) :: t -> M.trace "join" "handle var-const equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l - | (x, a, b, ((None,_,_) as rhs), ((Some _,_,_) as rhs')) :: t -> M.trace "join" "handle const-var equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l - | (x, a, b, ((None,o1,d1) as rhs), ((None,o2,d2) )) :: t when (o1,d1)=(o2,d2) -> M.trace "join" "handle const-const equivalence group" ; List.fold (fun acc e -> modify acc x rhs e) map l - | (x, a, b, ((None,_,_) as rhs), ((None,_,_) as rhs')) :: t -> M.trace "join" "handle const1-const2 equivalence group" ; List.fold (fun acc e -> modify acc x rhs e) map l + | (x, a, b, ((Some _,_,_) as rhs), ((Some _,_,_) as rhs')) :: t -> if M.tracing then M.trace "join" "handle var-var equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l + | (x, a, b, ((Some _,_,_) as rhs), ((None,_,_) as rhs')) :: t -> if M.tracing then M.trace "join" "handle var-const equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l + | (x, a, b, ((None,_,_) as rhs), ((Some _,_,_) as rhs')) :: t -> if M.tracing then M.trace "join" "handle const-var equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l + | (x, a, b, ((None,o1,d1) as rhs), ((None,o2,d2) )) :: t when (o1,d1)=(o2,d2) -> if M.tracing then M.trace "join" "handle const-const equivalence group" ; List.fold (fun acc e -> modify acc x rhs e) map l + | (x, a, b, ((None,_,_) as rhs), ((None,_,_) as rhs')) :: t -> if M.tracing then M.trace "join" "handle const1-const2 equivalence group" ; List.fold (fun acc e -> modify acc x rhs e) map l | [] -> let exception EmptyComponent in raise EmptyComponent in Some (List.fold iterate (EConj.make_empty_conj @@ fst ad) new_components) From c6336515f17183f95e9dfc8a20e225067f94cc06 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Thu, 23 May 2024 22:13:41 +0200 Subject: [PATCH 122/566] join carried out for const cases --- .../apron/linearTwoVarEqualityDomain.apron.ml | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 89cead570b..6967d0371d 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -525,21 +525,21 @@ struct in (* Calculate new components as groups *) let new_components = BatList.group cmp_z table in - (* Adjust the domain map to represent the new components *) - let modify map x (refmonom, offs, divi) (idx, _, _, (monom1, z1, d1), (monom2, z2, d2)) = EConj.set_rhs map (idx) - (if monom1 = monom2 && Z.equal z1 z2 && Z.equal d1 d2 then (monom1, z1, d1) - else - let refcoeff = Option.map_default fst Z.one refmonom in - let coeff1 = Option.map_default fst Z.one monom1 in - (Some (Z.(coeff1*divi),x), Z.((z1*refcoeff)-(offs*coeff1)), Z.(refcoeff*d1)) ) - in + + (* ci1 = a*ch1+b /\ ci2 = a*ch2+b *) + (* ===> a = (ci1-ci2)/(ch1-ch2) b = ci2-a*ch2 *) + let constentry ci1 ci2 ch1 ch2 xh = + let a = Q.((ci1-ci2) / (ch1-ch2)) in + let b= Q.(ci2 - a*ch2) in + let anum=a.num and aden=a.den and bnum=b.num and bden=b.den in + Rhs.canonicalize (Some (Z.(anum*bden),xh),Z.(bnum*aden) ,Z.(aden*bden) ) in let iterate map l = match l with - | (x, a, b, ((Some _,_,_) as rhs), ((Some _,_,_) as rhs')) :: t -> if M.tracing then M.trace "join" "handle var-var equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l - | (x, a, b, ((Some _,_,_) as rhs), ((None,_,_) as rhs')) :: t -> if M.tracing then M.trace "join" "handle var-const equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l - | (x, a, b, ((None,_,_) as rhs), ((Some _,_,_) as rhs')) :: t -> if M.tracing then M.trace "join" "handle const-var equivalence group"; List.fold (fun acc e -> modify acc x rhs e) map l - | (x, a, b, ((None,o1,d1) as rhs), ((None,o2,d2) )) :: t when (o1,d1)=(o2,d2) -> if M.tracing then M.trace "join" "handle const-const equivalence group" ; List.fold (fun acc e -> modify acc x rhs e) map l - | (x, a, b, ((None,_,_) as rhs), ((None,_,_) as rhs')) :: t -> if M.tracing then M.trace "join" "handle const1-const2 equivalence group" ; List.fold (fun acc e -> modify acc x rhs e) map l + | (h, _, _, ((Some (ch,_),oh,dh)), ((Some _,_,_) )) :: t -> if M.tracing then M.trace "join" "handle var-var class"; List.fold (fun acc e -> acc) map t + | (h, _, _, ((Some (ch,_),oh,dh)), ((None,_,_) )) :: t -> if M.tracing then M.trace "join" "handle var-const class"; List.fold (fun acc e -> acc) map t + | (h, _, _, ((None,_,_) ), ((Some (ch,_),oh,dh))) :: t -> if M.tracing then M.trace "join" "handle const-var class"; List.fold (fun acc e -> acc) map t + | (_, _, _, rhs , rhs' ) :: t when rhs=rhs' -> if M.tracing then M.trace "join" "handle const-const class" ; List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l + | (h, _, _, ((None,oh1,dh1) ), ((None),oh2,dh2) ) :: t -> if M.tracing then M.trace "join" "handle const1-const2 class" ; List.fold (fun acc (i,_,_,(_,oi1,di1),(_,oi2,di2)) -> EConj.set_rhs acc i (constentry Q.(make oi1 di1) Q.(make oi2 di2) (Q.make oh1 dh1) (Q.make oh2 dh2) h)) map t | [] -> let exception EmptyComponent in raise EmptyComponent in Some (List.fold iterate (EConj.make_empty_conj @@ fst ad) new_components) From 9e4cd5892b9ba8f9ed1d0c97e4d16586b7bdfcc8 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 09:53:34 +0200 Subject: [PATCH 123/566] migration to appendix A - join (part 2) --- .../apron/linearTwoVarEqualityDomain.apron.ml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 6967d0371d..2e3b473fcc 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -525,21 +525,25 @@ struct in (* Calculate new components as groups *) let new_components = BatList.group cmp_z table in - + let varentry ci offi ch offh xh = + let (coeff,off,d) = Q.(ci,(offi*ch)-(ci*offh),ch) in (* compute new rhs in Q *) + let (coeff,off,d) = Z.(coeff.num*d.den*off.den,off.num*d.den*coeff.den,d. num*coeff.den*off.den) in (* convert that back into Z *) + Rhs.canonicalize (Some(coeff,xh),off,d) + in (* ci1 = a*ch1+b /\ ci2 = a*ch2+b *) (* ===> a = (ci1-ci2)/(ch1-ch2) b = ci2-a*ch2 *) let constentry ci1 ci2 ch1 ch2 xh = let a = Q.((ci1-ci2) / (ch1-ch2)) in - let b= Q.(ci2 - a*ch2) in + let b = Q.(ci2 - a*ch2) in let anum=a.num and aden=a.den and bnum=b.num and bden=b.den in Rhs.canonicalize (Some (Z.(anum*bden),xh),Z.(bnum*aden) ,Z.(aden*bden) ) in let iterate map l = match l with - | (h, _, _, ((Some (ch,_),oh,dh)), ((Some _,_,_) )) :: t -> if M.tracing then M.trace "join" "handle var-var class"; List.fold (fun acc e -> acc) map t - | (h, _, _, ((Some (ch,_),oh,dh)), ((None,_,_) )) :: t -> if M.tracing then M.trace "join" "handle var-const class"; List.fold (fun acc e -> acc) map t - | (h, _, _, ((None,_,_) ), ((Some (ch,_),oh,dh))) :: t -> if M.tracing then M.trace "join" "handle const-var class"; List.fold (fun acc e -> acc) map t - | (_, _, _, rhs , rhs' ) :: t when rhs=rhs' -> if M.tracing then M.trace "join" "handle const-const class" ; List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l - | (h, _, _, ((None,oh1,dh1) ), ((None),oh2,dh2) ) :: t -> if M.tracing then M.trace "join" "handle const1-const2 class" ; List.fold (fun acc (i,_,_,(_,oi1,di1),(_,oi2,di2)) -> EConj.set_rhs acc i (constentry Q.(make oi1 di1) Q.(make oi2 di2) (Q.make oh1 dh1) (Q.make oh2 dh2) h)) map t + | (h, _, _, ((Some (ch,_),oh,dh)), ((Some _,_,_) )) :: t -> List.fold (fun acc (i,_,_,(Some (ci,_),oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make ci di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t + | (h, _, _, ((Some (ch,_),oh,dh)), ((None,_,_) )) :: t -> List.fold (fun acc (i,_,_,(Some (ci,_),oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make ci di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t + | (h, _, _, ((None,_,_) ), ((Some (ch,_),oh,dh))) :: t -> List.fold (fun acc (i,_,_,_,(Some (ci,_),oi,di)) -> EConj.set_rhs acc i (varentry Q.(make ci di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t + | (_, _, _, rhs , rhs' ) :: t when rhs=rhs' -> List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l + | (h, _, _, ((None,oh1,dh1) ), ((None),oh2,dh2) ) :: t -> List.fold (fun acc (i,_,_,(_,oi1,di1),(_,oi2,di2)) -> EConj.set_rhs acc i (constentry Q.(make oi1 di1) Q.(make oi2 di2) Q.(make oh1 dh1) Q.(make oh2 dh2) h)) map t | [] -> let exception EmptyComponent in raise EmptyComponent in Some (List.fold iterate (EConj.make_empty_conj @@ fst ad) new_components) From e290d3c2556fc91ec4a5f3371c65f8e2d23b5036 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 10:07:28 +0200 Subject: [PATCH 124/566] added regression test for coefficient-heavy transactions --- .../77-lin2vareq/34-coefficient-features.c | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/regression/77-lin2vareq/34-coefficient-features.c diff --git a/tests/regression/77-lin2vareq/34-coefficient-features.c b/tests/regression/77-lin2vareq/34-coefficient-features.c new file mode 100644 index 0000000000..4a0469a40d --- /dev/null +++ b/tests/regression/77-lin2vareq/34-coefficient-features.c @@ -0,0 +1,69 @@ +//SKIP PARAM: --set ana.activated[+] lin2vareq --set sem.int.signed_overflow assume_none +// this test checks basic coefficient handing in main and join capabilities in loop + +#include + +void loop() { + int random; + int i = 0; + int x = 0; + int y = 0; + + x=x+4; + y=y+8; + i=i+1; + + if (random) { + x=x+4; + y=y+8; + i=i+1; + } + + __goblint_check(x == 4*i); //SUCCESS + + for(i = 1; i < 100; i++) { + x=x+4; + y=y+8; + + __goblint_check(y == 2*x); //SUCCESS + } + + x=0; + y=0; + + for(i = 1; i < 100; i++) { + x=x+4; + y=y+8; + + __goblint_check(y == 2*x); //SUCCESS + __goblint_check(x == 4*i); //SUCCESS + } +} + +void main() { + int a; + int b; + int c; + int unknown; + a = 4; + + b = 4*c; + + __goblint_check(b == 4*c); //SUCCESS + + b = a*c; + + __goblint_check(b == 4*c); //SUCCESS + + if (5*b == 20*unknown + a){ + + __goblint_check(5*b == 20*unknown + a); //SUCCESS + } + + b = unknown ? a*c : 4*c; + + __goblint_check(b == 4*c); //SUCCESS + + loop(); + +} \ No newline at end of file From 9de6f858fcdf8526208e5041aba22036517365ee Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 10:59:26 +0200 Subject: [PATCH 125/566] make coefficents in regression coprime, and flex with propagation of 2var relations --- tests/regression/77-lin2vareq/34-coefficient-features.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/regression/77-lin2vareq/34-coefficient-features.c b/tests/regression/77-lin2vareq/34-coefficient-features.c index 4a0469a40d..b415f4398a 100644 --- a/tests/regression/77-lin2vareq/34-coefficient-features.c +++ b/tests/regression/77-lin2vareq/34-coefficient-features.c @@ -26,6 +26,10 @@ void loop() { y=y+8; __goblint_check(y == 2*x); //SUCCESS + + int res = (y==2*x )+ 1; + + __goblint_check(res); //SUCCESS } x=0; @@ -55,9 +59,9 @@ void main() { __goblint_check(b == 4*c); //SUCCESS - if (5*b == 20*unknown + a){ + if (7*b == 20*unknown + a){ - __goblint_check(5*b == 20*unknown + a); //SUCCESS + __goblint_check(7*b == 20*unknown + a); //SUCCESS } b = unknown ? a*c : 4*c; From 3291633f1a1dde4fff71751ac567f5986b05d0e1 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 11:00:06 +0200 Subject: [PATCH 126/566] wave through var-var equivalences if var1=var2 --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 2e3b473fcc..8dc4a71e90 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -539,10 +539,10 @@ struct Rhs.canonicalize (Some (Z.(anum*bden),xh),Z.(bnum*aden) ,Z.(aden*bden) ) in let iterate map l = match l with + | (_, _, _, rhs , rhs' ) :: t when rhs=rhs' -> List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l | (h, _, _, ((Some (ch,_),oh,dh)), ((Some _,_,_) )) :: t -> List.fold (fun acc (i,_,_,(Some (ci,_),oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make ci di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t | (h, _, _, ((Some (ch,_),oh,dh)), ((None,_,_) )) :: t -> List.fold (fun acc (i,_,_,(Some (ci,_),oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make ci di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t | (h, _, _, ((None,_,_) ), ((Some (ch,_),oh,dh))) :: t -> List.fold (fun acc (i,_,_,_,(Some (ci,_),oi,di)) -> EConj.set_rhs acc i (varentry Q.(make ci di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t - | (_, _, _, rhs , rhs' ) :: t when rhs=rhs' -> List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l | (h, _, _, ((None,oh1,dh1) ), ((None),oh2,dh2) ) :: t -> List.fold (fun acc (i,_,_,(_,oi1,di1),(_,oi2,di2)) -> EConj.set_rhs acc i (constentry Q.(make oi1 di1) Q.(make oi2 di2) Q.(make oh1 dh1) Q.(make oh2 dh2) h)) map t | [] -> let exception EmptyComponent in raise EmptyComponent in From 83d7fb8f263a64b71e10872ae5691d56e08ee327 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 11:05:21 +0200 Subject: [PATCH 127/566] removed warning 8 --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 8dc4a71e90..9d744ad926 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -540,9 +540,9 @@ struct let iterate map l = match l with | (_, _, _, rhs , rhs' ) :: t when rhs=rhs' -> List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l - | (h, _, _, ((Some (ch,_),oh,dh)), ((Some _,_,_) )) :: t -> List.fold (fun acc (i,_,_,(Some (ci,_),oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make ci di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t - | (h, _, _, ((Some (ch,_),oh,dh)), ((None,_,_) )) :: t -> List.fold (fun acc (i,_,_,(Some (ci,_),oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make ci di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t - | (h, _, _, ((None,_,_) ), ((Some (ch,_),oh,dh))) :: t -> List.fold (fun acc (i,_,_,_,(Some (ci,_),oi,di)) -> EConj.set_rhs acc i (varentry Q.(make ci di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t + | (h, _, _, ((Some (ch,_),oh,dh)), ((Some _,_,_) )) :: t -> List.fold (fun acc (i,_,_,(monom,oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make (fst@@Option.get monom) di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t + | (h, _, _, ((Some (ch,_),oh,dh)), ((None,_,_) )) :: t -> List.fold (fun acc (i,_,_,(monom,oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make (fst@@Option.get monom) di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t + | (h, _, _, ((None,_,_) ), ((Some (ch,_),oh,dh))) :: t -> List.fold (fun acc (i,_,_,_,(monom,oi,di)) -> EConj.set_rhs acc i (varentry Q.(make (fst@@Option.get monom) di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t | (h, _, _, ((None,oh1,dh1) ), ((None),oh2,dh2) ) :: t -> List.fold (fun acc (i,_,_,(_,oi1,di1),(_,oi2,di2)) -> EConj.set_rhs acc i (constentry Q.(make oi1 di1) Q.(make oi2 di2) Q.(make oh1 dh1) Q.(make oh2 dh2) h)) map t | [] -> let exception EmptyComponent in raise EmptyComponent in From 16c8e6567a09836a684e56c0a3fcd8a5a55a0933 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 13:25:47 +0200 Subject: [PATCH 128/566] got rid of unnecessarily largearray --- .../apron/linearTwoVarEqualityDomain.apron.ml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 9d744ad926..d3beadbb9f 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -290,7 +290,6 @@ struct in let rec convert_texpr texp = begin match texp with - (* If x is a constant, replace it with its const. val. immediately *) | Cst (Interval _) -> failwith "constant was an interval; this is not supported" | Cst (Scalar x) -> begin match SharedFunctions.int_of_scalar ?round:None x with @@ -322,15 +321,15 @@ struct BatOption.bind (monomials_from_texp t texp) (fun monomiallist -> let d = Option.get t.d in - let expr = Array.make (Environment.size t.env) (Q.zero) in (*TODO*: migrate to map; array of coeff/divisor per var idx*) - let accumulate_constants (aconst,adiv) (v,offs,divi) = match v with - | None -> let gcdee = Z.gcd adiv divi in (Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi) + let accumulate_constants (exprcache,(aconst,adiv)) (v,offs,divi) = match v with + | None -> let gcdee = Z.gcd adiv divi in exprcache,(Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi) | Some (coeff,idx) -> let (somevar,someoffs,somedivi)=Rhs.subst (EConj.get_rhs d idx) idx (v,offs,divi) in (* normalize! *) - (Option.may (fun (coef,ter) -> expr.(ter) <- Q.(expr.(ter) + Q.make coef somedivi)) somevar; - let gcdee = Z.gcd adiv divi in (Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi)) + let newcache = Option.map_default (fun (coef,ter) -> EConj.IntMap.add ter Q.((EConj.IntMap.find_default zero ter exprcache) + make coef somedivi) exprcache) exprcache somevar in + let gcdee = Z.gcd adiv divi in + (newcache,(Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi)) in - let constant = List.fold_left accumulate_constants (Z.zero,Z.one) monomiallist in (* abstract simplification of the guard wrt. reference variables *) - Some (Array.fold_lefti (fun list v (c) -> if Q.equal c Q.zero then list else (c.num,v,c.den)::list) [] expr, constant) ) + let (expr,constant) = List.fold_left accumulate_constants (EConj.IntMap.empty,(Z.zero,Z.one)) monomiallist in (* abstract simplification of the guard wrt. reference variables *) + Some (EConj.IntMap.fold (fun v c acc -> if Q.equal c Q.zero then acc else (Q.num c,v,Q.den c)::acc) expr [], constant) ) let simplified_monomials_from_texp (t: t) texp = let res = simplified_monomials_from_texp t texp in @@ -348,7 +347,6 @@ struct let simplify_to_ref_and_offset t texp = timing_wrap "coeff_vec" (simplify_to_ref_and_offset t) texp - (* Copy because function is not "with" so should not mutate inputs *) let assign_const t var const divi = match t.d with | None -> t | Some t_d -> {d = Some (EConj.set_rhs t_d var (None, const, divi)); env = t.env} From aded7d59eaa7153134b6f71ed89aae85e9eea893 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 14:03:46 +0200 Subject: [PATCH 129/566] rm redundancies --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index d3beadbb9f..5bd68cc858 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -416,10 +416,9 @@ struct subscr i let show_var env i = - let transl = [|"₀";"₁";"₂";"₃";"₄";"₅";"₆";"₇";"₈";"₉"|] in let res = Var.to_string (Environment.var_of_dim env i) in match String.split_on_char '#' res with - | varname::rest::[] -> varname ^ (try String.fold_left (fun acc c -> acc ^ transl.(Char.code c - Char.code '0')) "" rest with _ -> "#"^rest) + | varname::rest::[] -> varname ^ (try to_subscript @@ int_of_string rest with _ -> "#" ^ rest) | _ -> res (** prints the current variable equalities with resolved variable names *) From 26419aa1bd1645cbb766be67464f60b7c2e73610 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Fri, 24 May 2024 15:30:28 +0200 Subject: [PATCH 130/566] Update src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml make repopulation of the sparse conjunction more concise Co-authored-by: Michael Schwarz --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 5bd68cc858..d73d9cf783 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -501,11 +501,11 @@ struct - rhs2 however, we have to account for the sparseity of EConj maps by manually patching holes with default values *) let joinfunction lhs rhs1 rhs2 = - (match rhs1,rhs2 with (* first of all re-instantiate implicit sparse elements *) - | Some a, Some b -> Some (a,b) - | None, Some b -> Some (Rhs.var_zero lhs,b) - | Some a, None -> Some (a,Rhs.var_zero lhs) - | _ -> None) + ( + let e = Option.default (Rhs.var_zero lhs) in + match rhs1,rhs2 with (* first of all re-instantiate implicit sparse elements *) + | None, None -> None + | a, b -> Some (e a, e b)) |> BatOption.map (fun (r1,r2) -> match (r1,r2) with (* criterion A , criterion B *) | (Some (c1,_),o1,d1), (Some (c2,_),o2,d2)-> lhs, Q.make Z.((o1*d2)-(o2*d1)) Z.(c1*d2), Q.make Z.(c2*d2) Z.(c1*d1), r1, r2 From 49895fe04666fdfa4a82d3a7323c7692931ccbfd Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 15:25:25 +0200 Subject: [PATCH 131/566] replace = with more specialized Rhs.equal --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index d73d9cf783..b2beb9fab9 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -536,7 +536,7 @@ struct Rhs.canonicalize (Some (Z.(anum*bden),xh),Z.(bnum*aden) ,Z.(aden*bden) ) in let iterate map l = match l with - | (_, _, _, rhs , rhs' ) :: t when rhs=rhs' -> List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l + | (_, _, _, rhs , rhs' ) :: t when Rhs.equal rhs rhs' -> List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l | (h, _, _, ((Some (ch,_),oh,dh)), ((Some _,_,_) )) :: t -> List.fold (fun acc (i,_,_,(monom,oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make (fst@@Option.get monom) di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t | (h, _, _, ((Some (ch,_),oh,dh)), ((None,_,_) )) :: t -> List.fold (fun acc (i,_,_,(monom,oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make (fst@@Option.get monom) di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t | (h, _, _, ((None,_,_) ), ((Some (ch,_),oh,dh))) :: t -> List.fold (fun acc (i,_,_,_,(monom,oi,di)) -> EConj.set_rhs acc i (varentry Q.(make (fst@@Option.get monom) di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t From 7e2a741eedcb942c16720181ebd37f7d1a849cf3 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 15:27:52 +0200 Subject: [PATCH 132/566] gotten rid of silly record deconstruction --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index b2beb9fab9..090f31718e 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -532,8 +532,7 @@ struct let constentry ci1 ci2 ch1 ch2 xh = let a = Q.((ci1-ci2) / (ch1-ch2)) in let b = Q.(ci2 - a*ch2) in - let anum=a.num and aden=a.den and bnum=b.num and bden=b.den in - Rhs.canonicalize (Some (Z.(anum*bden),xh),Z.(bnum*aden) ,Z.(aden*bden) ) in + Rhs.canonicalize (Some (Z.(a.num*b.den),xh),Z.(b.num*a.den) ,Z.(a.den*b.den) ) in let iterate map l = match l with | (_, _, _, rhs , rhs' ) :: t when Rhs.equal rhs rhs' -> List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l From 62e569b0767332a8321d735f91b213977629171d Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 15:34:53 +0200 Subject: [PATCH 133/566] make indentation happy again --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 090f31718e..23ee5056be 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -504,8 +504,8 @@ struct ( let e = Option.default (Rhs.var_zero lhs) in match rhs1,rhs2 with (* first of all re-instantiate implicit sparse elements *) - | None, None -> None - | a, b -> Some (e a, e b)) + | None, None -> None + | a, b -> Some (e a, e b)) |> BatOption.map (fun (r1,r2) -> match (r1,r2) with (* criterion A , criterion B *) | (Some (c1,_),o1,d1), (Some (c2,_),o2,d2)-> lhs, Q.make Z.((o1*d2)-(o2*d1)) Z.(c1*d2), Q.make Z.(c2*d2) Z.(c1*d1), r1, r2 From 4810b5da346eb66ae59d59063a6961710b0138b4 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 15:39:19 +0200 Subject: [PATCH 134/566] make gcd more compact --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 23ee5056be..266f8e0505 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -38,10 +38,8 @@ module Rhs = struct (** factor out gcd from all terms, i.e. ax=by+c is the canonical form for adx+bdy+cd *) let canonicalize (v,o,d) = let gcd = Z.gcd o d in - let gcd = match v with - | Some (c,_) -> Z.gcd c gcd - | None -> gcd - in let gcd = if (Z.(lt d Z.zero)) then Z.neg gcd else gcd in + let gcd = Option.map_default (fun (c,_) -> Z.gcd c gcd) gcd v in + let gcd = if (Z.(lt d Z.zero)) then Z.neg gcd else gcd in (BatOption.map (fun (coeff,i) -> (Z.div coeff gcd,i)) v,Z.div o gcd, Z.div d gcd) (** Substitute rhs for varx in rhs' *) From 98ae9a4bb46b0976e9491b91f3bb2d50b6c0319a Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 15:49:00 +0200 Subject: [PATCH 135/566] explain canonicalization --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 266f8e0505..5aef1b1371 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -37,10 +37,10 @@ module Rhs = struct (** factor out gcd from all terms, i.e. ax=by+c is the canonical form for adx+bdy+cd *) let canonicalize (v,o,d) = - let gcd = Z.gcd o d in - let gcd = Option.map_default (fun (c,_) -> Z.gcd c gcd) gcd v in - let gcd = if (Z.(lt d Z.zero)) then Z.neg gcd else gcd in - (BatOption.map (fun (coeff,i) -> (Z.div coeff gcd,i)) v,Z.div o gcd, Z.div d gcd) + let gcd = Z.gcd o d in (* gcd of coefficients *) + let gcd = Option.map_default (fun (c,_) -> Z.gcd c gcd) gcd v in (* include monomial in gcd computation *) + let commondivisor = if Z.(lt d zero) then Z.neg gcd else gcd in (* cannonical form dictates d being positive *) + (BatOption.map (fun (coeff,i) -> (Z.div coeff commondivisor,i)) v,Z.div o commondivisor, Z.div d commondivisor) (** Substitute rhs for varx in rhs' *) let subst rhs varx rhs' = From 55c15f74bcffa6adc5d8360b1a9d88be1f7b0a31 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 24 May 2024 16:21:23 +0200 Subject: [PATCH 136/566] printf tuning --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 5aef1b1371..50fdf54d99 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -29,7 +29,7 @@ module Rhs = struct else (Z.to_string c) ^"·" let show_rhs_formatted formatter = function | (Some (coeff,v), o,_) when Z.equal o Z.zero -> Printf.sprintf "%s%s" (show_coeff coeff) (formatter v) - | (Some (coeff,v), o,_) -> Printf.sprintf "%s%s%+Ld" (show_coeff coeff) (formatter v) (Z.to_int64 o) + | (Some (coeff,v), o,_) -> Printf.sprintf "%s%s%+Lu" (show_coeff coeff) (formatter v) (Z.to_int64_unsigned o) | (None, o,_) -> Printf.sprintf "%Ld" (Z.to_int64 o) let show (v,o,d) = let rhs=show_rhs_formatted (Printf.sprintf "var_%d") (v,o,d) in From 9078c2a6e087e8ae7ef5bf30d2d01f6caee168da Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Sat, 25 May 2024 22:33:36 +0200 Subject: [PATCH 137/566] oversight of %+, so lets do it the affeq way --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 50fdf54d99..81ae290f66 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -27,9 +27,10 @@ module Rhs = struct if Z.equal c Z.one then "" else if Z.equal c Z.minus_one then "-" else (Z.to_string c) ^"·" - let show_rhs_formatted formatter = function + let show_rhs_formatted formatter = let ztostring n = if Z.(geq n zero) then "+" else "" ^ Z.to_string n in + function | (Some (coeff,v), o,_) when Z.equal o Z.zero -> Printf.sprintf "%s%s" (show_coeff coeff) (formatter v) - | (Some (coeff,v), o,_) -> Printf.sprintf "%s%s%+Lu" (show_coeff coeff) (formatter v) (Z.to_int64_unsigned o) + | (Some (coeff,v), o,_) -> Printf.sprintf "%s%s %s" (show_coeff coeff) (formatter v) (ztostring o) | (None, o,_) -> Printf.sprintf "%Ld" (Z.to_int64 o) let show (v,o,d) = let rhs=show_rhs_formatted (Printf.sprintf "var_%d") (v,o,d) in From e0e9c3436e8652df23476e177b22e6e507e9ab7e Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Sat, 25 May 2024 23:02:13 +0200 Subject: [PATCH 138/566] point out IMap has nothing directly to do with EConj --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 81ae290f66..f89a8d39ba 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -316,19 +316,20 @@ struct | x -> Some(x) (** convert and simplify (wrt. reference variables) a texpr into a tuple of a list of monomials (coeff,varidx,divi) and a (constant/divi) *) - let simplified_monomials_from_texp (t: t) texp = + let simplified_monomials_from_texp (t: t) texp = BatOption.bind (monomials_from_texp t texp) (fun monomiallist -> let d = Option.get t.d in + let module IMap = EConj.IntMap in let accumulate_constants (exprcache,(aconst,adiv)) (v,offs,divi) = match v with | None -> let gcdee = Z.gcd adiv divi in exprcache,(Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi) | Some (coeff,idx) -> let (somevar,someoffs,somedivi)=Rhs.subst (EConj.get_rhs d idx) idx (v,offs,divi) in (* normalize! *) - let newcache = Option.map_default (fun (coef,ter) -> EConj.IntMap.add ter Q.((EConj.IntMap.find_default zero ter exprcache) + make coef somedivi) exprcache) exprcache somevar in + let newcache = Option.map_default (fun (coef,ter) -> IMap.add ter Q.((IMap.find_default zero ter exprcache) + make coef somedivi) exprcache) exprcache somevar in let gcdee = Z.gcd adiv divi in (newcache,(Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi)) in - let (expr,constant) = List.fold_left accumulate_constants (EConj.IntMap.empty,(Z.zero,Z.one)) monomiallist in (* abstract simplification of the guard wrt. reference variables *) - Some (EConj.IntMap.fold (fun v c acc -> if Q.equal c Q.zero then acc else (Q.num c,v,Q.den c)::acc) expr [], constant) ) + let (expr,constant) = List.fold_left accumulate_constants (IMap.empty,(Z.zero,Z.one)) monomiallist in (* abstract simplification of the guard wrt. reference variables *) + Some (IMap.fold (fun v c acc -> if Q.equal c Q.zero then acc else (Q.num c,v,Q.den c)::acc) expr [], constant) ) let simplified_monomials_from_texp (t: t) texp = let res = simplified_monomials_from_texp t texp in From c6ec44bb6686c0123cfd819892fa15607a024cb2 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Sat, 25 May 2024 23:13:37 +0200 Subject: [PATCH 139/566] more concise subst --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f89a8d39ba..87446a7b6b 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -46,8 +46,7 @@ module Rhs = struct (** Substitute rhs for varx in rhs' *) let subst rhs varx rhs' = match rhs,rhs' with - | (Some (c,x),o,d),(Some (c',x'),o',d') when x'=varx -> canonicalize (Some (Z.mul c c',x),Z.((o*c')+(d*o')),Z.mul d d') - | (None ,o,d),(Some (c',x'),o',d') when x'=varx -> canonicalize (None ,Z.((o*c')+(d*o')),Z.mul d d') + | (monom,o,d),(Some (c',x'),o',d') when x'=varx -> canonicalize (Option.map (fun (c,x) -> (Z.mul c c',x)) monom,Z.((o*c')+(d*o')),Z.mul d d') | _ -> rhs' end From 6f02c2ab45d901bc78eedf35908b1bc9f7dd71ce Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Sat, 25 May 2024 23:24:20 +0200 Subject: [PATCH 140/566] rm faulty eval_int answer --- .../apron/linearTwoVarEqualityDomain.apron.ml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 87446a7b6b..99fc69a0a5 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -158,8 +158,8 @@ module EqualitiesConjunction = struct List.fold (fun map i -> let (oldref,c',a') = (get_rhs d i) in let (b',_) = BatOption.get oldref in - let newrhs = (Some (Z.(b'*a),head), Z.(c' - (b' * c)), Z.(a'*b)) in - canonicalize_and_set map i newrhs + (Some (Z.(b'*a),head), Z.(c' - (b' * c)), Z.(a'*b)) |> + canonicalize_and_set map i ) d cluster (* shift offset to match new reference variable *) | [] -> d) (* empty cluster means no work for us *) | _ -> d) (* variable is either a constant or expressed by another refvar *) in @@ -364,13 +364,6 @@ struct | Some (None, offset, divisor) when Z.equal (Z.rem offset divisor) Z.zero -> let res = Z.div offset divisor in (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string res) (IntOps.BigIntOps.to_string res); Some res, Some res) - | Some (None, offset, divisor) -> let res = Z.div offset divisor in - let (lower,upper) = if Z.lt res Z.zero then - (Z.pred res,res) - else - (res,Z.succ res) in - (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string lower) (IntOps.BigIntOps.to_string upper); - Some lower, Some upper) | _ -> None, None let bound_texpr d texpr1 = timing_wrap "bounds calculation" (bound_texpr d) texpr1 From 31bb80289111c688bbfc04cbf27a9965d1c43ca8 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Mon, 27 May 2024 16:11:30 +0200 Subject: [PATCH 141/566] rm Z.t->int conv issues --- src/cdomains/apron/gobApron.apron.ml | 7 +++++++ src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml index c39a3e42db..e202a88c60 100644 --- a/src/cdomains/apron/gobApron.apron.ml +++ b/src/cdomains/apron/gobApron.apron.ml @@ -1,6 +1,13 @@ open Batteries include Apron +module Coeff = +struct + include Coeff + + let s_of_z z = Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z z)) +end + module Var = struct include Var diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 99fc69a0a5..08358005b4 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -31,7 +31,7 @@ module Rhs = struct function | (Some (coeff,v), o,_) when Z.equal o Z.zero -> Printf.sprintf "%s%s" (show_coeff coeff) (formatter v) | (Some (coeff,v), o,_) -> Printf.sprintf "%s%s %s" (show_coeff coeff) (formatter v) (ztostring o) - | (None, o,_) -> Printf.sprintf "%Ld" (Z.to_int64 o) + | (None, o,_) -> Printf.sprintf "%s" (Z.to_string o) let show (v,o,d) = let rhs=show_rhs_formatted (Printf.sprintf "var_%d") (v,o,d) in if not (Z.equal d Z.one) then "(" ^ rhs ^ ")/" ^ (Z.to_string d) else rhs @@ -798,12 +798,12 @@ struct let get_const acc i = function | (None, o, d) -> let xi = Environment.var_of_dim t.env i in - of_coeff xi [(Coeff.s_of_int (- (Z.to_int d)), xi)] o :: acc + of_coeff xi [(GobApron.Coeff.s_of_z @@ Z.neg d, xi)] o :: acc | (Some (c,r), _,_) when r = i -> acc | (Some (c,r), o, d) -> let xi = Environment.var_of_dim t.env i in let ri = Environment.var_of_dim t.env r in - of_coeff xi [(Coeff.s_of_int (- (Z.to_int d)), xi); (Coeff.s_of_int @@ Z.to_int c, ri)] o :: acc + of_coeff xi [(GobApron.Coeff.s_of_z @@ Z.neg d, xi); (GobApron.Coeff.s_of_z c, ri)] o :: acc in BatOption.get t.d |> fun (_,map) -> EConj.IntMap.fold (fun lhs rhs list -> get_const list lhs rhs) map [] From 9b4b255f29f887b5cffb38280e7ebf86ed7cdd4c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 27 May 2024 17:45:58 +0200 Subject: [PATCH 142/566] Fix `mutex-meet` for malloc after thread creation --- src/analyses/apron/relationPriv.apron.ml | 9 +++++++-- tests/regression/46-apron2/89-malloc.c | 21 +++++++++++++++++++++ tests/regression/46-apron2/90-malloc2.c | 21 +++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 tests/regression/46-apron2/89-malloc.c create mode 100644 tests/regression/46-apron2/90-malloc2.c diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 61da6ddc42..046e1230d7 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -479,7 +479,7 @@ struct let get_mutex_inits' = keep_only_protected_globals ask m get_mutex_inits in RD.join get_m get_mutex_inits' - let get_mutex_global_g_with_mutex_inits ask getg g = + let get_mutex_global_g_with_mutex_inits (ask:Q.ask) getg g = let g_var = AV.global g in let get_mutex_global_g = if Param.handle_atomic then ( @@ -487,7 +487,12 @@ struct RD.keep_vars (getg (V.mutex atomic_mutex)) [g_var] ) else - getg (V.global g) + let r = getg (V.global g) in + if RD.is_bot r && (ask.f (Queries.IsAllocVar g)) then + (* malloc'ed blobs may not have a value here yet *) + RD.top () + else + r in let get_mutex_inits = getg V.mutex_inits in let get_mutex_inits' = RD.keep_vars get_mutex_inits [g_var] in diff --git a/tests/regression/46-apron2/89-malloc.c b/tests/regression/46-apron2/89-malloc.c new file mode 100644 index 0000000000..8780568748 --- /dev/null +++ b/tests/regression/46-apron2/89-malloc.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --set ana.apron.domain interval --set sem.int.signed_overflow assume_none +// Checks that assinging to malloc'ed memory does not cause both branches to be dead +#include +#include +void nop(void* arg) { +} + +void main() { + pthread_t thread; + pthread_create(&thread, 0, &nop, 0); + + long *k = malloc(sizeof(long)); + *k = 5; + if (1) + ; + + __goblint_check(*k >= 5); // Reachable and true + + *k = *k+1; + __goblint_check(*k >= 5); // Reachable and true +} diff --git a/tests/regression/46-apron2/90-malloc2.c b/tests/regression/46-apron2/90-malloc2.c new file mode 100644 index 0000000000..36696956e7 --- /dev/null +++ b/tests/regression/46-apron2/90-malloc2.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid --set ana.apron.domain interval --set sem.int.signed_overflow assume_none +// Checks that assinging to malloc'ed memory does not cause both branches to be dead +#include +#include +void nop(void* arg) { +} + +void main() { + pthread_t thread; + pthread_create(&thread, 0, &nop, 0); + + long *k = malloc(sizeof(long)); + *k = 5; + if (1) + ; + + __goblint_check(*k >= 5); // Reachable and true + + *k = *k+1; + __goblint_check(*k >= 5); // Reachable and true +} From 896f236c98ba829aee25e5f02cb08d4bb2bba9b1 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 27 May 2024 17:55:00 +0200 Subject: [PATCH 143/566] Also fix atomic --- src/analyses/apron/relationPriv.apron.ml | 21 ++++++++++--------- tests/regression/46-apron2/91-malloc-atomic.c | 21 +++++++++++++++++++ 2 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 tests/regression/46-apron2/91-malloc-atomic.c diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 046e1230d7..78a06dc227 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -482,17 +482,18 @@ struct let get_mutex_global_g_with_mutex_inits (ask:Q.ask) getg g = let g_var = AV.global g in let get_mutex_global_g = - if Param.handle_atomic then ( - (* Unprotected invariant is one big relation. *) - RD.keep_vars (getg (V.mutex atomic_mutex)) [g_var] - ) - else - let r = getg (V.global g) in - if RD.is_bot r && (ask.f (Queries.IsAllocVar g)) then - (* malloc'ed blobs may not have a value here yet *) - RD.top () + let r = + if Param.handle_atomic then + (* Unprotected invariant is one big relation. *) + RD.keep_vars (getg (V.mutex atomic_mutex)) [g_var] else - r + getg (V.global g) + in + if RD.is_bot r && (ask.f (Queries.IsAllocVar g)) then + (* malloc'ed blobs may not have a value here yet *) + RD.top () + else + r in let get_mutex_inits = getg V.mutex_inits in let get_mutex_inits' = RD.keep_vars get_mutex_inits [g_var] in diff --git a/tests/regression/46-apron2/91-malloc-atomic.c b/tests/regression/46-apron2/91-malloc-atomic.c new file mode 100644 index 0000000000..b2f057f6a4 --- /dev/null +++ b/tests/regression/46-apron2/91-malloc-atomic.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-atomic --set ana.apron.domain interval --set sem.int.signed_overflow assume_none +// Checks that assinging to malloc'ed memory does not cause both branches to be dead +#include +#include +void nop(void* arg) { +} + +void main() { + pthread_t thread; + pthread_create(&thread, 0, &nop, 0); + + long *k = malloc(sizeof(long)); + *k = 5; + if (1) + ; + + __goblint_check(*k >= 5); // Reachable and true + + *k = *k+1; + __goblint_check(*k >= 5); // Reachable and true +} From ab78123429e6805415a97a4ecd737008ff3a5411 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 28 May 2024 10:32:19 +0200 Subject: [PATCH 144/566] [skip ci] int_of_scalar now returning Q.t --- src/cdomains/apron/sharedFunctions.apron.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 827dc252fc..b287bb9bae 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -20,21 +20,21 @@ let int_of_scalar ?round (scalar: Scalar.t) = | None when Stdlib.Float.is_integer f -> Some f | None -> None in - Z.of_float f + Q.make (Z.of_float f) Z.one | Mpqf scalar -> (* octMPQ, boxMPQ, polkaMPQ *) let n = Mpqf.get_num scalar in let d = Mpqf.get_den scalar in - let+ z = + let+ (n,d) = if Mpzf.cmp_int d 1 = 0 then (* exact integer (denominator 1) *) - Some n + Some (n,Mpzf.of_int 1) else begin match round with - | Some `Floor -> Some (Mpzf.fdiv_q n d) (* floor division *) - | Some `Ceil -> Some (Mpzf.cdiv_q n d) (* ceiling division *) - | None -> None + | Some `Floor -> Some (Mpzf.fdiv_q n d, Mpzf.of_int 1) (* floor division *) + | Some `Ceil -> Some (Mpzf.cdiv_q n d, Mpzf.of_int 1) (* ceiling division *) + | None -> Some (n,d) end in - Z_mlgmpidl.z_of_mpzf z + Q.make (Z_mlgmpidl.z_of_mpzf n) (Z_mlgmpidl.z_of_mpzf d) | _ -> failwith ("int_of_scalar: unsupported: " ^ Scalar.to_string scalar) From ec3deedeef7eba8f2e6f8f8d03c71cb1e500910e Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 28 May 2024 14:59:19 +0200 Subject: [PATCH 145/566] wrong brackets --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 08358005b4..0a110a5999 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -27,7 +27,7 @@ module Rhs = struct if Z.equal c Z.one then "" else if Z.equal c Z.minus_one then "-" else (Z.to_string c) ^"·" - let show_rhs_formatted formatter = let ztostring n = if Z.(geq n zero) then "+" else "" ^ Z.to_string n in + let show_rhs_formatted formatter = let ztostring n = (if Z.(geq n zero) then "+" else "") ^ Z.to_string n in function | (Some (coeff,v), o,_) when Z.equal o Z.zero -> Printf.sprintf "%s%s" (show_coeff coeff) (formatter v) | (Some (coeff,v), o,_) -> Printf.sprintf "%s%s %s" (show_coeff coeff) (formatter v) (ztostring o) From 075f2e858293e899f6f27f066629d27a2f62b306 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 28 May 2024 15:57:04 +0200 Subject: [PATCH 146/566] fix unmatched monom in forget_variable --- .../apron/linearTwoVarEqualityDomain.apron.ml | 6 ++++-- .../regression/77-lin2vareq/34-coefficient-features.c | 11 ----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 0a110a5999..2da7deb5cc 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -104,7 +104,7 @@ module EqualitiesConjunction = struct let offsetlist = Array.to_list indexes in let rec bumpvar delta i = function (* bump the variable i by delta; find delta by counting indices in offsetlist until we reach a larger index then our current parameter *) | head::rest when i>=head -> bumpvar (delta+1) i rest (* rec call even when =, in order to correctly interpret double bumps *) - | _ (* i op i delta + | _ (* i let res = op i delta in res in let memobumpvar = (* Memoized version of bumpvar *) let module IntHash = struct type t = int [@@deriving eq,hash] end in @@ -145,9 +145,11 @@ module EqualitiesConjunction = struct (let ref_var_opt = Tuple3.first (get_rhs d var) in match ref_var_opt with | Some (_,ref_var) when ref_var = var -> + if M.tracing then M.trace "forget" "headvar var_%d" var; (* var is the reference variable of its connected component *) (let cluster = IntMap.fold - (fun i (ref,_,_) l -> if ref = ref_var_opt then i::l else l) (snd d) [] in + (fun i (ref,_,_) l -> BatOption.map_default (fun (coeff,ref) -> if (ref=ref_var) then i::l else l) l ref) (snd d) [] in + if M.tracing then M.trace "forget" "cluster varindices: [%s]" (String.concat ", " (List.map (string_of_int) cluster)); (* obtain cluster with common reference variable ref_var*) match cluster with (* new ref_var is taken from head of the cluster *) | head :: _ -> diff --git a/tests/regression/77-lin2vareq/34-coefficient-features.c b/tests/regression/77-lin2vareq/34-coefficient-features.c index b415f4398a..561eccf0af 100644 --- a/tests/regression/77-lin2vareq/34-coefficient-features.c +++ b/tests/regression/77-lin2vareq/34-coefficient-features.c @@ -21,17 +21,6 @@ void loop() { __goblint_check(x == 4*i); //SUCCESS - for(i = 1; i < 100; i++) { - x=x+4; - y=y+8; - - __goblint_check(y == 2*x); //SUCCESS - - int res = (y==2*x )+ 1; - - __goblint_check(res); //SUCCESS - } - x=0; y=0; From 94ebf865fc261825d3d137f4d8203a5da8be1ba3 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 29 May 2024 10:57:03 +0200 Subject: [PATCH 147/566] fixed broken is_top --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 2da7deb5cc..f6ae9b2c51 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -400,7 +400,7 @@ struct let top () = {d = Some (EConj.empty()); env = empty_env} (** is_top returns true for top_of array and empty array; precondition: t.env and t.d are of same size *) - let is_top t = Environment.equal empty_env t.env && GobOption.exists EConj.is_top_con t.d + let is_top t = GobOption.exists EConj.is_top_con t.d let to_subscript i = let transl = [|"₀";"₁";"₂";"₃";"₄";"₅";"₆";"₇";"₈";"₉"|] in @@ -472,8 +472,8 @@ struct | Some (coeffj,j) -> tuple_cmp (EConj.get_rhs ts i) @@ Rhs.subst (EConj.get_rhs ts j) j (var, offs, divi) in if env_comp = -2 || env_comp > 0 then false else - if is_bot_env t1 || is_top t2 then true else - if is_bot_env t2 || is_top t1 then false else + if is_bot_env t1 || is_top t2 then true + else if is_bot_env t2 || is_top t1 then false else let m1, m2 = Option.get t1.d, Option.get t2.d in let m1' = if env_comp = 0 then m1 else VarManagement.dim_add (Environment.dimchange t1.env t2.env) m1 in EConj.IntMap.for_all (implies m1') (snd m2) (* even on sparse m2, it suffices to check the non-trivial equalities, still present in sparse m2 *) From 9663b1c772277fc3eb80b1a0ce4e3b7fd19bf46d Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 29 May 2024 13:17:04 +0200 Subject: [PATCH 148/566] make combine tracing in relational analysis more specific --- src/analyses/apron/relationAnalysis.apron.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 6c118dac7a..97fd66d676 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -388,11 +388,11 @@ struct let st = ctx.local in let reachable_from_args = reachable_from_args ctx args in let fundec = Node.find_fundec ctx.node in - if M.tracing then M.tracel "combine" "relation f: %a" CilType.Varinfo.pretty f.svar; - if M.tracing then M.tracel "combine" "relation formals: %a" (d_list "," CilType.Varinfo.pretty) f.sformals; - if M.tracing then M.tracel "combine" "relation args: %a" (d_list "," d_exp) args; - if M.tracing then M.tracel "combine" "relation st: %a" D.pretty st; - if M.tracing then M.tracel "combine" "relation fun_st: %a" D.pretty fun_st; + if M.tracing then M.tracel "combine-rel" "relation f: %a" CilType.Varinfo.pretty f.svar; + if M.tracing then M.tracel "combine-rel" "relation formals: %a" (d_list "," CilType.Varinfo.pretty) f.sformals; + if M.tracing then M.tracel "combine-rel" "relation args: %a" (d_list "," d_exp) args; + if M.tracing then M.tracel "combine-rel" "relation st: %a" D.pretty st; + if M.tracing then M.tracel "combine-rel" "relation fun_st: %a" D.pretty fun_st; let new_fun_rel = RD.add_vars fun_st.rel (RD.vars st.rel) in let arg_substitutes = let filter_actuals (x,e) = @@ -418,7 +418,7 @@ struct in let any_local_reachable = any_local_reachable fundec reachable_from_args in let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in - if M.tracing then M.tracel "combine" "relation remove vars: %a" (docList (fun v -> Pretty.text (Apron.Var.to_string v))) arg_vars; + if M.tracing then M.tracel "combine-rel" "relation remove vars: %a" (docList (fun v -> Pretty.text (Apron.Var.to_string v))) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) let tainted = f_ask.f Queries.MayBeTainted in let tainted_vars = TaintPartialContexts.conv_varset tainted in @@ -432,7 +432,7 @@ struct ) in let unify_rel = RD.unify new_rel new_fun_rel in (* TODO: unify_with *) - if M.tracing then M.tracel "combine" "relation unifying %a %a = %a" RD.pretty new_rel RD.pretty new_fun_rel RD.pretty unify_rel; + if M.tracing then M.tracel "combine-rel" "relation unifying %a %a = %a" RD.pretty new_rel RD.pretty new_fun_rel RD.pretty unify_rel; {fun_st with rel = unify_rel} let combine_assign ctx r fe f args fc fun_st (f_ask : Queries.ask) = From caa8437fcbdbd2182c2f8a6aa50ea33a72ad58cb Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 29 May 2024 13:18:04 +0200 Subject: [PATCH 149/566] forget did not forget about the actual head-variable of the new cluster --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index f6ae9b2c51..d2489689e1 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -152,17 +152,18 @@ module EqualitiesConjunction = struct if M.tracing then M.trace "forget" "cluster varindices: [%s]" (String.concat ", " (List.map (string_of_int) cluster)); (* obtain cluster with common reference variable ref_var*) match cluster with (* new ref_var is taken from head of the cluster *) - | head :: _ -> + | head :: clusterrest -> (* ax = by + c /\ a'z = b'y + c' *) (* ==[[ y:=? ]]==> (a'b)z = (b'a)x + c' -(b'c) *) let (newref,c,a) = (get_rhs d head) in (* take offset between old and new reference variable *) let (b,_) = BatOption.get newref in - List.fold (fun map i -> + let shifted_cluster = (List.fold (fun map i -> (* shift offset to match new reference variable *) let (oldref,c',a') = (get_rhs d i) in let (b',_) = BatOption.get oldref in (Some (Z.(b'*a),head), Z.(c' - (b' * c)), Z.(a'*b)) |> canonicalize_and_set map i - ) d cluster (* shift offset to match new reference variable *) + ) d clusterrest) in + set_rhs shifted_cluster head (Rhs.var_zero head) (* finally make sure that head is now trivial *) | [] -> d) (* empty cluster means no work for us *) | _ -> d) (* variable is either a constant or expressed by another refvar *) in let res = (fst res, IntMap.remove var (snd res)) in (* set d(var) to unknown, finally *) From e1462b308d7695b37ac9041b1a465c70bdc15657 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Wed, 29 May 2024 14:58:40 +0200 Subject: [PATCH 150/566] fixed forget_variable by a) sorting vars in cluster and b) rehauling cluster transformation with subst/inverse convenience functions --- .../apron/linearTwoVarEqualityDomain.apron.ml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index d2489689e1..923229ca96 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -147,21 +147,21 @@ module EqualitiesConjunction = struct | Some (_,ref_var) when ref_var = var -> if M.tracing then M.trace "forget" "headvar var_%d" var; (* var is the reference variable of its connected component *) - (let cluster = IntMap.fold + (let cluster = List.sort (Int.compare) @@ IntMap.fold (fun i (ref,_,_) l -> BatOption.map_default (fun (coeff,ref) -> if (ref=ref_var) then i::l else l) l ref) (snd d) [] in if M.tracing then M.trace "forget" "cluster varindices: [%s]" (String.concat ", " (List.map (string_of_int) cluster)); (* obtain cluster with common reference variable ref_var*) match cluster with (* new ref_var is taken from head of the cluster *) | head :: clusterrest -> - (* ax = by + c /\ a'z = b'y + c' *) - (* ==[[ y:=? ]]==> (a'b)z = (b'a)x + c' -(b'c) *) - let (newref,c,a) = (get_rhs d head) in (* take offset between old and new reference variable *) - let (b,_) = BatOption.get newref in - let shifted_cluster = (List.fold (fun map i -> (* shift offset to match new reference variable *) - let (oldref,c',a') = (get_rhs d i) in - let (b',_) = BatOption.get oldref in - (Some (Z.(b'*a),head), Z.(c' - (b' * c)), Z.(a'*b)) |> - canonicalize_and_set map i + (* head: divi*x = coeff*y + offs *) + (* divi*x = coeff*y + offs =inverse=> y =( divi*x - offs)/coeff *) + let (newref,offs,divi) = (get_rhs d head) in + let (coeff,y) = BatOption.get newref in + let (y,yrhs)= inverse head (coeff,y,offs,divi) in (* reassemble yrhs out of components *) + let shifted_cluster = (List.fold (fun map i -> + let irhs = (get_rhs d i) in (* old entry is i = irhs *) + Rhs.subst yrhs y irhs |> (* new entry for i is irhs [yrhs/y] *) + set_rhs map i ) d clusterrest) in set_rhs shifted_cluster head (Rhs.var_zero head) (* finally make sure that head is now trivial *) | [] -> d) (* empty cluster means no work for us *) @@ -240,7 +240,7 @@ module EqualitiesConjunction = struct let (_,newh1)= inverse i (coeff1,h1,o1,divi1) in let newh1 = Rhs.subst normalizedj j (Rhs.subst (Some(coeff,j),offs,divi) i newh1) in subst_var ts h1 newh1)) in - if M.tracing then M.trace "meet_with_one_conj" "meet_with_one_conj conj: %s eq: var_%d=%s -> %s " (show (snd ts)) i (Rhs.show (var,offs,divi)) (show (snd res)) + if M.tracing then M.tracel "meet_with_one_conj" "meet_with_one_conj conj: %s eq: var_%d=%s -> %s " (show (snd ts)) i (Rhs.show (var,offs,divi)) (show (snd res)) ; res (** affine transform variable i allover conj with transformer (Some (coeff,i)+offs)/divi *) From 35a4382efc2509e46a64edc7dc2effa8fc10060f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 31 May 2024 14:31:54 +0300 Subject: [PATCH 151/566] Simplify SetDomain.FiniteSet arguments --- src/domain/setDomain.ml | 12 ++++++------ tests/unit/maindomaintest.ml | 9 +++------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/domain/setDomain.ml b/src/domain/setDomain.ml index 1b5239de80..9b545a78ee 100644 --- a/src/domain/setDomain.ml +++ b/src/domain/setDomain.ml @@ -436,23 +436,23 @@ struct include Lattice.Reverse (Base) end -module type FiniteSetElems = +module type FiniteSetElem = sig - type t + include Printable.S val elems: t list + (** List of all possible elements. *) end -(* TODO: put elems into E *) -module FiniteSet (E:Printable.S) (Elems:FiniteSetElems with type t = E.t) = +module FiniteSet (E: FiniteSetElem) = struct module E = struct include E - let arbitrary () = QCheck.oneofl Elems.elems + let arbitrary () = QCheck.oneofl E.elems end include Make (E) - let top () = of_list Elems.elems + let top () = of_list E.elems let is_top x = equal x (top ()) end diff --git a/tests/unit/maindomaintest.ml b/tests/unit/maindomaintest.ml index 8e6a7f5a3a..8e1db76b83 100644 --- a/tests/unit/maindomaintest.ml +++ b/tests/unit/maindomaintest.ml @@ -15,14 +15,11 @@ struct let show = show end include Printable.SimpleShow (P) + + let elems = ['a'; 'b'; 'c'; 'd'] end -module ArbitraryLattice = SetDomain.FiniteSet (PrintableChar) ( - struct - type t = char - let elems = ['a'; 'b'; 'c'; 'd'] - end - ) +module ArbitraryLattice = SetDomain.FiniteSet (PrintableChar) module HoareArbitrary = HoareDomain.Set_LiftTop (ArbitraryLattice) (struct let topname = "Top" end) module HoareArbitrary_NoTop = HoareDomain.Set (ArbitraryLattice) From 07f539ffd4433913c087fb5d99f9bb5e5db3bc3f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 31 May 2024 14:33:39 +0300 Subject: [PATCH 152/566] Update first analysis tutorial regarding SetDomain.FiniteSet --- docs/developer-guide/firstanalysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/firstanalysis.md b/docs/developer-guide/firstanalysis.md index 4eb35e7f5d..b32538134b 100644 --- a/docs/developer-guide/firstanalysis.md +++ b/docs/developer-guide/firstanalysis.md @@ -80,4 +80,4 @@ For example, have a look at the following program: `tests/regression/99-tutorial _Hint:_ The easiest way to do this is to use the powerset lattice of `{-, 0, +}`. For example, "non-negative" is represented by `{0, +}`, while negative is represented by `{-}`. -To do this, modify `SL` by using `SetDomain.FiniteSet` (takes a `struct` with a list of finite elements as second parameter) instead of `Lattice.Flat` and reimplementing the two functions using `singleton` and `for_all`. +To do this, modify `SL` by using `SetDomain.FiniteSet` (which needs a finite list of elements to be added to `Signs`) instead of `Lattice.Flat` and reimplementing the two functions using `singleton` and `for_all`. From 325dd23a98666c21bf619fe9b08ca1f610a8ded0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 31 May 2024 14:47:29 +0300 Subject: [PATCH 153/566] Fix typo in first analysis tutorial --- docs/developer-guide/firstanalysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/firstanalysis.md b/docs/developer-guide/firstanalysis.md index b32538134b..c9e3c58720 100644 --- a/docs/developer-guide/firstanalysis.md +++ b/docs/developer-guide/firstanalysis.md @@ -74,7 +74,7 @@ For more information on the signature of the individual transfer functions, plea ## Extending the domain You could now enrich the lattice to also have a representation for non-negative (i.e., zero or positive) values. -Then the join of `Zero` and `Pos` would be "non-negative" instead of `Top`, allowing you to prove that such join is greated than `Neg`. +Then the join of `Zero` and `Pos` would be "non-negative" instead of `Top`, allowing you to prove that such join is greater than `Neg`. For example, have a look at the following program: `tests/regression/99-tutorials/02-first-extend.c`. _Hint:_ From 24d273bd0b44e98f523d6fea680ec5afb77768de Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 31 May 2024 14:47:53 +0300 Subject: [PATCH 154/566] Remove unnecessary warn.debug from first analysis tutorial --- docs/developer-guide/firstanalysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/firstanalysis.md b/docs/developer-guide/firstanalysis.md index c9e3c58720..bd4dfb3c11 100644 --- a/docs/developer-guide/firstanalysis.md +++ b/docs/developer-guide/firstanalysis.md @@ -35,7 +35,7 @@ This program is in the Goblint repository: `tests/regression/99-tutorials/01-fir But if you run Goblint out of the box on this example, it will not work: ```console -./goblint --enable warn.debug tests/regression/99-tutorials/01-first.c +./goblint tests/regression/99-tutorials/01-first.c ``` This will claim that the assertion in unknown. From 1082f1c2afeb10ab5f8ddd8fb77a63b3e64b4bc6 Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Mon, 3 Jun 2024 08:48:25 +0200 Subject: [PATCH 155/566] add missing stable_remove hook to destabilize_vs --- src/solver/td3.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/solver/td3.ml b/src/solver/td3.ml index 7e2a18b042..c7bec621e3 100644 --- a/src/solver/td3.ml +++ b/src/solver/td3.ml @@ -284,7 +284,11 @@ module Base = let was_stable = HM.mem stable y in HM.remove stable y; HM.remove superstable y; - HM.mem called y || destabilize_vs y || b || was_stable && List.mem_cmp S.Var.compare y vs + Hooks.stable_remove y; + if not (HM.mem called y) then + destabilize_vs y || b || was_stable && List.mem_cmp S.Var.compare y vs + else + true ) w false and solve ?reuse_eq x phase = if tracing then trace "sol2" "solve %a, phase: %s, called: %b, stable: %b, wpoint: %b" S.Var.pretty_trace x (show_phase phase) (HM.mem called x) (HM.mem stable x) (HM.mem wpoint x); From a50dc813320c2714b6bb0414e62a9897cc140370 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 3 Jun 2024 11:38:52 +0300 Subject: [PATCH 156/566] Unpin ppx_deriving --- dune-project | 2 +- goblint.opam | 4 +--- goblint.opam.locked | 8 ++------ goblint.opam.template | 2 -- gobview | 2 +- 5 files changed, 5 insertions(+), 13 deletions(-) diff --git a/dune-project b/dune-project index 878abd3b4f..c5adaddf81 100644 --- a/dune-project +++ b/dune-project @@ -29,7 +29,7 @@ (zarith (>= 1.10)) (yojson (>= 2.0.0)) (qcheck-core (>= 0.19)) - ppx_deriving + (ppx_deriving (>= 6.0.2)) (ppx_deriving_hash (>= 0.1.2)) (ppx_deriving_yojson (>= 3.7.0)) (ounit2 :with-test) diff --git a/goblint.opam b/goblint.opam index 692625c965..0d8a561fa0 100644 --- a/goblint.opam +++ b/goblint.opam @@ -27,7 +27,7 @@ depends: [ "zarith" {>= "1.10"} "yojson" {>= "2.0.0"} "qcheck-core" {>= "0.19"} - "ppx_deriving" + "ppx_deriving" {>= "6.0.2"} "ppx_deriving_hash" {>= "0.1.2"} "ppx_deriving_yojson" {>= "3.7.0"} "ounit2" {with-test} @@ -79,8 +79,6 @@ available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] - # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.locked b/goblint.opam.locked index f8de683948..d532457ced 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -81,10 +81,10 @@ depends: [ "ounit2" {= "2.2.6" & with-test} "pp" {= "1.1.2"} "ppx_derivers" {= "1.2.1"} - "ppx_deriving" {= "5.2.1"} + "ppx_deriving" {= "6.0.2"} "ppx_deriving_hash" {= "0.1.2"} "ppx_deriving_yojson" {= "3.7.0"} - "ppxlib" {= "0.28.0"} + "ppxlib" {= "0.32.1"} "qcheck-core" {= "0.20"} "qcheck-ounit" {= "0.20" & with-test} "re" {= "1.10.4" & with-doc} @@ -136,9 +136,5 @@ pin-depends: [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] - [ - "ppx_deriving.5.2.1" - "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" - ] ] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.template b/goblint.opam.template index 2d5ef10bc9..a730d5c064 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -4,8 +4,6 @@ available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] - # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/gobview b/gobview index 72abbb576b..195c61cbae 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 72abbb576b8ffc8759dcfa31501738f6b561b05a +Subproject commit 195c61cbae86b8ffb8af4a21f2acd689665c1c0e From 83d771b63a6c38edc5e344b078f59d989c81786f Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Mon, 3 Jun 2024 14:52:57 +0300 Subject: [PATCH 157/566] Improve zeroinit output by removing negation --- src/analyses/base.ml | 11 ++++++----- src/cdomain/value/cdomains/valueDomain.ml | 23 ++++++++++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 67e6276239..c33aaa4abb 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -17,6 +17,7 @@ module IdxDom = ValueDomain.IndexDomain module AD = ValueDomain.AD module Addr = ValueDomain.Addr module Offs = ValueDomain.Offs +module ZeroInit = ValueDomain.ZeroInit module LF = LibraryFunctions module CArrays = ValueDomain.CArrays module PU = PrecisionUtil @@ -2652,7 +2653,7 @@ struct | Some lv -> let heap_var = AD.of_var (heap_var true ctx) in (* ignore @@ printf "alloca will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) - set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true)); + set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, ZeroInit.malloc)); (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] | _ -> st end @@ -2665,7 +2666,7 @@ struct else AD.of_var (heap_var false ctx) in (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) - set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, true)); + set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, ZeroInit.malloc)); (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] | _ -> st end @@ -2682,7 +2683,7 @@ struct let countval = eval_int ~ctx st n in if ID.to_int countval = Some Z.one then ( set_many ~ctx st [ - (add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, false)); + (add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, ZeroInit.calloc)); (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var))) ] ) @@ -2690,7 +2691,7 @@ struct let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) set_many ~ctx st [ - (add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.one) (Blob (VD.bot (), blobsize, false)))); + (add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.one) (Blob (VD.bot (), blobsize, ZeroInit.calloc)))); (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero, `NoOffset))))) ] ) @@ -2713,7 +2714,7 @@ struct let p_addr' = AD.remove NullPtr p_addr in (* realloc with NULL is same as malloc, remove to avoid unknown value from NullPtr access *) let p_addr_get = get ~ctx st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) let size_int = eval_int ~ctx st size in - let heap_val:value = Blob (p_addr_get, size_int, true) in (* copy old contents with new size *) + let heap_val:value = Blob (p_addr_get, size_int, ZeroInit.malloc) in (* copy old contents with new size *) let heap_addr = AD.of_var (heap_var false ctx) in let heap_addr' = if get_bool "sem.malloc.fail" then diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index de64fde807..12c86abf60 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -57,11 +57,24 @@ sig val invalidate_value: VDQ.t -> typ -> t -> t end -(* ZeroInit is true if malloc was used to allocate memory and it's false if calloc was used *) -module ZeroInit = +module type ZeroInit = +sig + include Lattice.S + + val is_malloc : t -> bool + val malloc : t + val calloc : t +end + +(* ZeroInit is false if malloc was used to allocate memory and true if calloc was used *) +module ZeroInit : ZeroInit = struct include Lattice.Fake(Basetype.RawBools) - let name () = "no zeroinit" + let name () = "zeroinit" + + let is_malloc x = not x + let malloc = false + let calloc = true end module Blob (Value: S) (Size: IntDomain.Z)= @@ -862,8 +875,8 @@ struct end | _, _ -> None - let zero_init_calloced_memory orig x t = - if orig then + let zero_init_calloced_memory zeroinit x t = + if ZeroInit.is_malloc zeroinit then (* This Blob came from malloc *) x else if x = Bot then From d19f9c9c8bab362a43a0b6cd1d819540d475bc07 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Mon, 3 Jun 2024 15:10:49 +0300 Subject: [PATCH 158/566] Rename origin -> zeroinit --- src/cdomain/value/cdomains/valueDomain.ml | 46 +++++++++++------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index 12c86abf60..dd8ac844e3 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -49,8 +49,8 @@ module type Blob = sig type value type size - type origin - include Lattice.S with type t = value * size * origin + type zeroinit + include Lattice.S with type t = value * size * zeroinit val map: (value -> value) -> t -> t val value: t -> value @@ -83,7 +83,7 @@ struct let name () = "blob" type value = Value.t type size = Size.t - type origin = ZeroInit.t + type zeroinit = ZeroInit.t let map f (v, s, o) = f v, s, o let value (a, b, c) = a @@ -891,23 +891,23 @@ struct if M.tracing then M.traceli "eval_offset" "do_eval_offset %a %a (%a)" pretty x Offs.pretty offs (Pretty.docOpt (CilType.Exp.pretty ())) exp; let r = match x, offs with - | Blob((va, _, orig) as c), `Index (_, ox) -> + | Blob((va, _, zeroinit) as c), `Index (_, ox) -> begin let l', o' = shift_one_over l o in let ev = do_eval_offset ask f (Blobs.value c) ox exp l' o' v t in - zero_init_calloced_memory orig ev t + zero_init_calloced_memory zeroinit ev t end - | Blob((va, _, orig) as c), `Field _ -> + | Blob((va, _, zeroinit) as c), `Field _ -> begin let l', o' = shift_one_over l o in let ev = do_eval_offset ask f (Blobs.value c) offs exp l' o' v t in - zero_init_calloced_memory orig ev t + zero_init_calloced_memory zeroinit ev t end - | Blob((va, _, orig) as c), `NoOffset -> + | Blob((va, _, zeroinit) as c), `NoOffset -> begin let l', o' = shift_one_over l o in let ev = do_eval_offset ask f (Blobs.value c) offs exp l' o' v t in - zero_init_calloced_memory orig ev t + zero_init_calloced_memory zeroinit ev t end | Bot, _ -> Bot | _ -> @@ -965,24 +965,24 @@ struct let update_offset ?(blob_destructive=false) (ask: VDQ.t) (x:t) (offs:offs) (value:t) (exp:exp option) (v:lval) (t:typ): t = let rec do_update_offset (ask:VDQ.t) (x:t) (offs:offs) (value:t) (exp:exp option) (l:lval option) (o:offset option) (v:lval) (t:typ):t = if M.tracing then M.traceli "update_offset" "do_update_offset %a %a (%a) %a" pretty x Offs.pretty offs (Pretty.docOpt (CilType.Exp.pretty ())) exp pretty value; - let mu = function Blob (Blob (y, s', orig), s, orig2) -> Blob (y, ID.join s s',orig) | x -> x in + let mu = function Blob (Blob (y, s', zeroinit), s, _) -> Blob (y, ID.join s s', zeroinit) | x -> x in let r = match x, offs with | Mutex, _ -> (* hide mutex structure contents, not updated anyway *) Mutex - | Blob (x,s,orig), `Index (_,ofs) -> + | Blob (x,s,zeroinit), `Index (_,ofs) -> begin let l', o' = shift_one_over l o in - let x = zero_init_calloced_memory orig x t in - mu (Blob (join x (do_update_offset ask x ofs value exp l' o' v t), s, orig)) + let x = zero_init_calloced_memory zeroinit x t in + mu (Blob (join x (do_update_offset ask x ofs value exp l' o' v t), s, zeroinit)) end - | Blob (x,s,orig), `Field(f, _) -> + | Blob (x,s,zeroinit), `Field(f, _) -> begin (* We only have Blob for dynamically allocated memory. In these cases t is the type of the lval used to access it, i.e. for a struct s {int x; int y;} a; accessed via a->x *) (* will be int. Here, we need a zero_init of the entire contents of the blob though, which we get by taking the associated f.fcomp. Putting [] for attributes is ok, as we don't *) (* consider them in VD *) let l', o' = shift_one_over l o in - let x = zero_init_calloced_memory orig x (TComp (f.fcomp, [])) in + let x = zero_init_calloced_memory zeroinit x (TComp (f.fcomp, [])) in (* Strong update of scalar variable is possible if the variable is unique and size of written value matches size of blob being written to. *) let do_strong_update = match v with @@ -996,14 +996,14 @@ struct | _ -> false in if do_strong_update then - Blob ((do_update_offset ask x offs value exp l' o' v t), s, orig) + Blob ((do_update_offset ask x offs value exp l' o' v t), s, zeroinit) else - mu (Blob (join x (do_update_offset ask x offs value exp l' o' v t), s, orig)) + mu (Blob (join x (do_update_offset ask x offs value exp l' o' v t), s, zeroinit)) end - | Blob (x,s,orig), _ -> + | Blob (x,s,zeroinit), _ -> begin let l', o' = shift_one_over l o in - let x = zero_init_calloced_memory orig x t in + let x = zero_init_calloced_memory zeroinit x t in (* Strong update of scalar variable is possible if the variable is unique and size of written value matches size of blob being written to. *) let do_strong_update = begin match v with @@ -1019,9 +1019,9 @@ struct end in if do_strong_update then - Blob ((do_update_offset ask x offs value exp l' o' v t), s, orig) + Blob ((do_update_offset ask x offs value exp l' o' v t), s, zeroinit) else - mu (Blob (join x (do_update_offset ask x offs value exp l' o' v t), s, orig)) + mu (Blob (join x (do_update_offset ask x offs value exp l' o' v t), s, zeroinit)) end | Thread _, _ -> (* hack for pthread_t variables *) @@ -1049,7 +1049,7 @@ struct match offs with | `NoOffset -> begin match value with - | Blob (y, s, orig) -> mu (Blob (join x y, s, orig)) + | Blob (y, s, zeroinit) -> mu (Blob (join x y, s, zeroinit)) | Int _ -> cast t value | _ -> value end @@ -1302,7 +1302,7 @@ and Unions: UnionDomain.S with type t = UnionDomain.Field.t * Compound.t and typ and CArrays: ArrayDomain.StrWithDomain with type value = Compound.t and type idx = ArrIdxDomain.t = ArrayDomain.AttributeConfiguredAndNullByteArrayDomain(Compound)(ArrIdxDomain) -and Blobs: Blob with type size = ID.t and type value = Compound.t and type origin = ZeroInit.t = Blob (Compound) (ID) +and Blobs: Blob with type size = ID.t and type value = Compound.t and type zeroinit = ZeroInit.t = Blob (Compound) (ID) module type InvariantArg = From 2a79e42601bcf03c3a446fd7dceafc22096358d1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 10:45:25 +0300 Subject: [PATCH 159/566] Add comment about multiple protecting mutexes for ghost invariants --- src/analyses/basePriv.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index c244e7f3bf..be261d96b7 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -868,6 +868,10 @@ struct Invariant.none (* don't output protected invariant because it's the same as unprotected *) else ( let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) + (* Very conservative about multiple (write-)protecting mutexes: invariant is not claimed when any of them is held. + It should be possible to be more precise because writes only happen with all of them held, + but conjunction is unsound when one of the mutexes is temporarily unlocked. + Hypothetical read-protection is also somehow relevant. *) Q.AD.fold (fun m acc -> if LockDomain.Addr.equal m (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) then acc From 86be1d04d4db322cc8bf4504a778f18e910586ac Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 11:35:58 +0300 Subject: [PATCH 160/566] Add unsound test for unlocking non-definite mutex in privatizations --- .../13-privatized/93-unlock-idx-ambiguous.c | 27 +++++++ .../13-privatized/93-unlock-idx-ambiguous.t | 75 +++++++++++++++++++ tests/regression/13-privatized/dune | 2 + 3 files changed, 104 insertions(+) create mode 100644 tests/regression/13-privatized/93-unlock-idx-ambiguous.c create mode 100644 tests/regression/13-privatized/93-unlock-idx-ambiguous.t create mode 100644 tests/regression/13-privatized/dune diff --git a/tests/regression/13-privatized/93-unlock-idx-ambiguous.c b/tests/regression/13-privatized/93-unlock-idx-ambiguous.c new file mode 100644 index 0000000000..081302514b --- /dev/null +++ b/tests/regression/13-privatized/93-unlock-idx-ambiguous.c @@ -0,0 +1,27 @@ +// PARAM: --set ana.base.privatization protection --enable ana.sv-comp.functions +#include +#include +extern _Bool __VERIFIER_nondet_bool(); + +int g; +pthread_mutex_t m[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[0]); + pthread_mutex_lock(&m[1]); // so we're unlocking a mutex we definitely hold + g++; + int r = __VERIFIER_nondet_bool(); + pthread_mutex_unlock(&m[r]); // TODO NOWARN (definitely held either way) + // could have unlocked m[0], so should have published g there + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m[0]); + __goblint_check(g == 0); // UNKNOWN! + pthread_mutex_unlock(&m[0]); + return 0; +} diff --git a/tests/regression/13-privatized/93-unlock-idx-ambiguous.t b/tests/regression/13-privatized/93-unlock-idx-ambiguous.t new file mode 100644 index 0000000000..f954ac1d4c --- /dev/null +++ b/tests/regression/13-privatized/93-unlock-idx-ambiguous.t @@ -0,0 +1,75 @@ +TODO: should not succeed + + $ goblint --set ana.base.privatization protection --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) + [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +TODO: should not succeed + + $ goblint --set ana.base.privatization mutex-meet --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) + [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +TODO: should not succeed + + $ goblint --set ana.base.privatization lock --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) + [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +TODO: should not succeed + + $ goblint --set ana.base.privatization write --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) + [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +TODO: should not succeed + + $ goblint --set ana.base.privatization mine-nothread --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) + [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + diff --git a/tests/regression/13-privatized/dune b/tests/regression/13-privatized/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/13-privatized/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) From 682eb0c183c897a31c8862c95a05bbc416437eb9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 13:15:22 +0300 Subject: [PATCH 161/566] Refactor many privatizations to use LockDomain.MustLockset --- src/analyses/apron/relationPriv.apron.ml | 4 +- src/analyses/basePriv.ml | 102 +++++++++++------------ src/analyses/commonPriv.ml | 14 ++-- src/cdomains/lockDomain.ml | 16 ++++ 4 files changed, 75 insertions(+), 61 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 61da6ddc42..cada5ff888 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -579,7 +579,7 @@ struct let lock ask getg (st: relation_components_t) m = let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in (* TODO: somehow actually unneeded here? *) - if not atomic && Locksets.(not (Lockset.mem m (current_lockset ask))) then ( + if not atomic && Locksets.(not (MustLockset.mem_addr m (current_lockset ask))) then ( let rel = st.rel in let get_m = get_m_with_mutex_inits ask getg m in (* Additionally filter get_m in case it contains variables it no longer protects. E.g. in 36/22. *) @@ -1089,7 +1089,7 @@ struct let lock ask getg (st: relation_components_t) m = let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in - if not atomic && Locksets.(not (Lockset.mem m (current_lockset ask))) then ( + if not atomic && Locksets.(not (MustLockset.mem_addr m (current_lockset ask))) then ( let rel = st.rel in let _,lmust,l = st.priv in let lm = LLock.mutex m in diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index cecc838b9e..f874232176 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -281,7 +281,7 @@ struct cpa' *) let lock ask getg (st: BaseComponents (D).t) m = - if Locksets.(not (Lockset.mem m (current_lockset ask))) then ( + if Locksets.(not (MustLockset.mem_addr m (current_lockset ask))) then ( let get_m = get_m_with_mutex_inits ask getg m in (* Really we want is_unprotected, but pthread_cond_wait emits unlock-lock events, where our (necessary) original context still has the mutex, @@ -377,7 +377,7 @@ struct cpa' *) let lock (ask: Queries.ask) getg (st: BaseComponents (D).t) m = - if Locksets.(not (Lockset.mem m (current_lockset ask))) then ( + if Locksets.(not (MustLockset.mem_addr m (current_lockset ask))) then ( let get_m = get_m_with_mutex_inits ask getg m in (* Additionally filter get_m in case it contains variables it no longer protects. *) let is_in_Gm x _ = is_protected_by ask m x in @@ -523,7 +523,7 @@ struct {st with cpa = cpa'; priv = (W.add x w,LMust.add lm lmust,l')} let lock (ask: Queries.ask) getg (st: BaseComponents (D).t) m = - if Locksets.(not (Lockset.mem m (current_lockset ask))) then ( + if Locksets.(not (MustLockset.mem_addr m (current_lockset ask))) then ( let _,lmust,l = st.priv in let lm = LLock.mutex m in let get_m = get_m_with_mutex_inits (not (LMust.mem lm lmust)) ask getg m in @@ -847,12 +847,12 @@ struct module GWeak = struct - include MapDomain.MapBot (Lockset) (WeakRange) + include MapDomain.MapBot (MustLockset) (WeakRange) let name () = "weak" end module GSync = struct - include MapDomain.MapBot (Lockset) (SyncRange) + include MapDomain.MapBot (MustLockset) (SyncRange) let name () = "synchronized" end module G = @@ -908,10 +908,10 @@ struct let invariant_vars ask getg st = let module VS = Set.Make (CilType.Varinfo) in let s = current_lockset ask in - Lockset.fold (fun m acc -> + MustLockset.fold (fun m acc -> GSync.fold (fun s' cpa' acc -> SyncRange.fold_sync_vars VS.add cpa' acc - ) (G.sync (getg (V.mutex m))) acc + ) (G.sync (getg (V.mutex_mustlock m))) acc ) s VS.empty |> VS.elements end @@ -987,7 +987,7 @@ struct let read_global ask getg (st: BaseComponents (D).t) x = let s = current_lockset ask in GWeak.fold (fun s' tm acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then ThreadMap.fold (fun t' v acc -> VD.join v acc ) tm acc @@ -1007,7 +1007,7 @@ struct let lock ask getg (st: BaseComponents (D).t) m = let s = current_lockset ask in let cpa' = GSync.fold (fun s' cpa' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then CPA.join cpa' acc else acc @@ -1016,13 +1016,13 @@ struct {st with cpa = cpa'} let unlock ask getg sideg (st: BaseComponents (D).t) m = - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove_addr m (current_lockset ask) in let t = current_thread ask in let side_cpa = CPA.filter (fun x _ -> GWeak.fold (fun s' tm acc -> (* TODO: swap 2^M and T partitioning for lookup by t here first? *) let v = ThreadMap.find t tm in - (Lockset.mem m s' && not (VD.is_bot v)) || acc + (MustLockset.mem_addr m s' && not (VD.is_bot v)) || acc ) (G.weak (getg (V.global x))) false ) st.cpa in @@ -1048,7 +1048,7 @@ struct let read_global ask getg (st: BaseComponents (D).t) x = let s = current_lockset ask in GWeak.fold (fun s' v acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then VD.join v acc else acc @@ -1065,7 +1065,7 @@ struct let lock ask getg (st: BaseComponents (D).t) m = let s = current_lockset ask in let cpa' = GSync.fold (fun s' cpa' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then CPA.join cpa' acc else acc @@ -1074,10 +1074,10 @@ struct {st with cpa = cpa'} let unlock ask getg sideg (st: BaseComponents (D).t) m = - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove_addr m (current_lockset ask) in let side_cpa = CPA.filter (fun x _ -> GWeak.fold (fun s' v acc -> - (Lockset.mem m s' && not (VD.is_bot v)) || acc + (MustLockset.mem_addr m s' && not (VD.is_bot v)) || acc ) (G.weak (getg (V.global x))) false ) st.cpa in @@ -1119,7 +1119,7 @@ struct let read_global ask getg (st: BaseComponents (D).t) x = let s = current_lockset ask in GWeak.fold (fun s' v acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then VD.join v acc else acc @@ -1140,7 +1140,7 @@ struct let lock ask getg (st: BaseComponents (D).t) m = let s = current_lockset ask in let cpa' = GSync.fold (fun s' cpa' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then CPA.join cpa' acc else acc @@ -1149,7 +1149,7 @@ struct {st with cpa = cpa'} let unlock ask getg sideg (st: BaseComponents (D).t) m = - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove_addr m (current_lockset ask) in let is_in_W x _ = W.mem x st.priv in let side_cpa = CPA.filter is_in_W st.cpa in sideg (V.mutex m) (G.create_sync (GSync.singleton s side_cpa)); @@ -1169,7 +1169,7 @@ struct if Param.side_effect_global_init then ( CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_global ask x then ( - sideg (V.global x) (G.create_weak (GWeak.singleton (Lockset.empty ()) v)); + sideg (V.global x) (G.create_weak (GWeak.singleton (MustLockset.empty ()) v)); {st with priv = W.add x st.priv} (* TODO: is this add necessary? *) ) else @@ -1222,7 +1222,7 @@ struct let startstate () = (DV.bot (), L.bot ()) - let lockset_init = Lockset.top () + let lockset_init = MustLockset.all () let distr_init getg x v = if get_bool "exp.priv-distr-init" then @@ -1241,7 +1241,7 @@ struct let syncs = UnwrappedG.sync (getg (V.mutex m)) in MinLocksets.fold (fun b acc -> GSync.fold (fun s' cpa' acc -> - if Lockset.disjoint b s' then + if MustLockset.disjoint b s' then let v = CPA.find x cpa' in VD.join v acc else @@ -1254,7 +1254,7 @@ struct in let weaks = UnwrappedG.weak (getg (V.global x)) in let d_weak = GWeak.fold (fun s' v acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then VD.join v acc else acc @@ -1301,7 +1301,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let sideg = Wrapper.sideg ask sideg in let getg = Wrapper.getg ask getg in - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove_addr m (current_lockset ask) in let is_in_G x _ = is_global ask x in let side_cpa = CPA.filter is_in_G st.cpa in let side_cpa = CPA.mapi (fun x v -> @@ -1355,13 +1355,13 @@ struct module GWeakW = struct - include MapDomain.MapBot (Lockset) (VD) + include MapDomain.MapBot (MustLockset) (VD) let fold_weak f m a = fold (fun _ v a -> f v a) m a end module GSyncW = struct - include MapDomain.MapBot (Lockset) (LockCenteredBase.CPA) + include MapDomain.MapBot (MustLockset) (LockCenteredBase.CPA) let fold_sync_vars f m a = fold (fun _ cpa a -> @@ -1393,11 +1393,11 @@ struct let startstate () = (W.bot (), P.top ()) - let lockset_init = Lockset.top () + let lockset_init = MustLockset.all () let distr_init getg x v = if get_bool "exp.priv-distr-init" then - let v_init = GWeakW.find lockset_init (GWeak.find (Lockset.empty ()) (UnwrappedG.weak (getg (V.global x)))) in + let v_init = GWeakW.find lockset_init (GWeak.find (MustLockset.empty ()) (UnwrappedG.weak (getg (V.global x)))) in VD.join v v_init else v @@ -1408,13 +1408,13 @@ struct let (w, p) = st.priv in let p_x = P.find x p in let d_cpa = CPA.find x st.cpa in - let d_sync = Lockset.fold (fun m acc -> - if MinLocksets.exists (fun s''' -> not (Lockset.mem m s''')) p_x then - let syncs = UnwrappedG.sync (getg (V.mutex m)) in + let d_sync = MustLockset.fold (fun m acc -> + if MinLocksets.exists (fun s''' -> not (MustLockset.mem m s''')) p_x then + let syncs = UnwrappedG.sync (getg (V.mutex_mustlock m)) in GSync.fold (fun s' gsyncw' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then GSyncW.fold (fun w' cpa' acc -> - if MinLocksets.exists (fun s'' -> Lockset.disjoint s'' w') p_x then + if MinLocksets.exists (fun s'' -> MustLockset.disjoint s'' w') p_x then let v = CPA.find x cpa' in VD.join v acc else @@ -1429,9 +1429,9 @@ struct in let weaks = UnwrappedG.weak (getg (V.global x)) in let d_weak = GWeak.fold (fun s' gweakw' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then GWeakW.fold (fun w' v acc -> - if MinLocksets.exists (fun s'' -> Lockset.disjoint s'' w') p_x then + if MinLocksets.exists (fun s'' -> MustLockset.disjoint s'' w') p_x then VD.join v acc else acc @@ -1471,7 +1471,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let getg = Wrapper.getg ask getg in let sideg = Wrapper.sideg ask sideg in - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove_addr m (current_lockset ask) in let (w, p) = st.priv in let p' = P.map (fun s' -> MinLocksets.add s s') p in if M.tracing then M.traceli "priv" "unlock %a %a" Lock.pretty m CPA.pretty st.cpa; @@ -1507,7 +1507,7 @@ struct if EscapeDomain.EscapedVars.mem x escaped then ( let (w, p) = st.priv in let p' = P.add x (MinLocksets.singleton s) p in - sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (Lockset.empty ()) (GWeakW.singleton lockset_init v))); + sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (MustLockset.empty ()) (GWeakW.singleton lockset_init v))); {st with cpa = CPA.remove x st.cpa; priv = (w, p')} ) else @@ -1518,7 +1518,7 @@ struct let sideg = Wrapper.sideg ask sideg in CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_global ask x then ( - sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (Lockset.empty ()) (GWeakW.singleton lockset_init v))); + sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (MustLockset.empty ()) (GWeakW.singleton lockset_init v))); {st with cpa = CPA.remove x st.cpa} ) else @@ -1548,11 +1548,11 @@ struct let startstate () = ((W.bot (), P.top ()), (DV.bot (), L.bot ())) - let lockset_init = Lockset.top () + let lockset_init = MustLockset.all () let distr_init getg x v = if get_bool "exp.priv-distr-init" then - let v_init = GWeakW.find lockset_init (GWeak.find (Lockset.empty ()) (UnwrappedG.weak (getg (V.global x)))) in + let v_init = GWeakW.find lockset_init (GWeak.find (MustLockset.empty ()) (UnwrappedG.weak (getg (V.global x)))) in VD.join v v_init else v @@ -1568,9 +1568,9 @@ struct let syncs = UnwrappedG.sync (getg (V.mutex m)) in MinLocksets.fold (fun b acc -> GSync.fold (fun s' gsyncw' acc -> - if Lockset.disjoint b s' then + if MustLockset.disjoint b s' then GSyncW.fold (fun w' cpa' acc -> - if MinLocksets.exists (fun s'' -> Lockset.disjoint s'' w') p_x then + if MinLocksets.exists (fun s'' -> MustLockset.disjoint s'' w') p_x then let v = CPA.find x cpa' in VD.join v acc else @@ -1586,9 +1586,9 @@ struct in let weaks = UnwrappedG.weak (getg (V.global x)) in let d_m_weak = GWeak.fold (fun s' gweakw' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then GWeakW.fold (fun w' v acc -> - if MinLocksets.exists (fun s'' -> Lockset.disjoint s'' w') p_x then + if MinLocksets.exists (fun s'' -> MustLockset.disjoint s'' w') p_x then VD.join v acc else acc @@ -1598,13 +1598,13 @@ struct ) weaks (VD.bot ()) in let d_m = VD.join d_m_sync d_m_weak in - let d_g_sync = Lockset.fold (fun m acc -> - if MinLocksets.exists (fun s''' -> not (Lockset.mem m s''')) p_x then - let syncs = UnwrappedG.sync (getg (V.mutex m)) in + let d_g_sync = MustLockset.fold (fun m acc -> + if MinLocksets.exists (fun s''' -> not (MustLockset.mem m s''')) p_x then + let syncs = UnwrappedG.sync (getg (V.mutex_mustlock m)) in GSync.fold (fun s' gsyncw' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then GSyncW.fold (fun w' cpa' acc -> - if MinLocksets.exists (fun s'' -> Lockset.disjoint s'' w') p_x then + if MinLocksets.exists (fun s'' -> MustLockset.disjoint s'' w') p_x then let v = CPA.find x cpa' in VD.join v acc else @@ -1656,7 +1656,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let getg = Wrapper.getg ask getg in let sideg = Wrapper.sideg ask sideg in - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove_addr m (current_lockset ask) in let ((w, p), vl) = st.priv in let p' = P.map (fun s' -> MinLocksets.add s s') p in let side_gsyncw = CPA.fold (fun x v acc -> @@ -1689,7 +1689,7 @@ struct if EscapeDomain.EscapedVars.mem x escaped then ( let ((w, p), (vv, l)) = st.priv in let p' = P.add x (MinLocksets.singleton s) p in - sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (Lockset.empty ()) (GWeakW.singleton lockset_init v))); + sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (MustLockset.empty ()) (GWeakW.singleton lockset_init v))); {st with cpa = CPA.remove x st.cpa; priv = ((w, p'), (vv, l))} ) else @@ -1700,7 +1700,7 @@ struct let sideg = Wrapper.sideg ask sideg in CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_global ask x then ( - sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (Lockset.empty ()) (GWeakW.singleton lockset_init v))); + sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (MustLockset.empty ()) (GWeakW.singleton lockset_init v))); {st with cpa = CPA.remove x st.cpa} ) else diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index aa829c4606..2334d76918 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -106,6 +106,7 @@ struct include Printable.Either3Conf (struct include Printable.DefaultConf let expand2 = false end) (VMutex) (VMutexInits) (VGlobal) let name () = "MutexGlobals" let mutex x: t = `Left x + let mutex_mustlock x = mutex (Addr (LockDomain.MustLock.to_mval x)) let mutex_inits: t = `Middle () let global x: t = `Right x end @@ -136,17 +137,14 @@ struct let name () = "lock" end - module Lockset = SetDomain.ToppedSet (Lock) (struct let topname = "All locks" end) + module MustLockset = LockDomain.MustLockset - module MustLockset = SetDomain.Reverse (Lockset) - - let current_lockset (ask: Q.ask): Lockset.t = + let current_lockset (ask: Q.ask): MustLockset.t = (* TODO: remove this global_init workaround *) if !AnalysisState.global_initialization then - Lockset.empty () + MustLockset.empty () else - let mls = ask.f Queries.MustLockset in - LockDomain.MustLockset.fold (fun ml acc -> Lockset.add (Addr (LockDomain.MustLock.to_mval ml)) acc) mls (Lockset.empty ()) (* TODO: use MustLockset as Lockset *) + ask.f Queries.MustLockset (* TODO: reversed SetDomain.Hoare *) module MinLocksets = HoareDomain.Set_LiftTop (MustLockset) (struct let topname = "All locksets" end) (* reverse Lockset because Hoare keeps maximal, but we need minimal *) @@ -171,7 +169,7 @@ struct let name () = "P" (* TODO: change MinLocksets.exists/top instead? *) - let find x p = find_opt x p |? MinLocksets.singleton (Lockset.empty ()) (* ensure exists has something to check for thread returns *) + let find x p = find_opt x p |? MinLocksets.singleton (MustLockset.empty ()) (* ensure exists has something to check for thread returns *) end end diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 35d73e5f28..1c9fcb98c7 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -35,6 +35,22 @@ module MustLockset = struct include SetDomain.Reverse (SetDomain.ToppedSet (MustLock) (struct let topname = "All mutexes" end)) + let mem_addr (a: Addr.t) (set: t) = (* TODO: replace with mem_mval *) + match Addr.to_mval a with + | Some mv when Mval.is_definite mv -> + let ml = MustLock.of_mval mv in + mem ml set + | _ -> + false + + let remove_addr (a: Addr.t) (set: t) = (* TODO: replace with remove_mval *) + match Addr.to_mval a with + | Some mv when Mval.is_definite mv -> + let ml = MustLock.of_mval mv in + remove ml set + | _ -> + set + let all (): t = `Top let is_all (set: t) = set = `Top end From e6429f02214a42d1f50bc1c1ab0d304018f516df Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 14:32:41 +0300 Subject: [PATCH 162/566] Fix base privatization unsoundness with non-definite mutex unlock --- src/analyses/base.ml | 34 +++++++++++++++---- .../13-privatized/93-unlock-idx-ambiguous.t | 20 +++-------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 67e6276239..13cf9f20da 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -3062,13 +3062,35 @@ struct match e with | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) if M.tracing then M.tracel "priv" "LOCK EVENT %a" LockDomain.Addr.pretty addr; - Priv.lock ask (priv_getg ctx.global) st addr + begin match addr with + | UnknownPtr -> ctx.local + | Addr (v, _) when ctx.ask (IsMultiple v) -> ctx.local + | Addr (v, o) when Addr.Mval.is_definite (v, o) -> + Priv.lock ask (priv_getg ctx.global) st addr + | _ -> ctx.local + end | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) - if addr = UnknownPtr then - M.info ~category:Unsound "Unknown mutex unlocked, base privatization unsound"; (* TODO: something more sound *) - WideningTokens.with_local_side_tokens (fun () -> - Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr - ) + begin match addr with + | UnknownPtr -> + M.info ~category:Unsound "Unknown mutex unlocked, base privatization unsound"; (* TODO: something more sound *) + ctx.local (* TODO: remove all! *) + | Addr (v, o) when Addr.Mval.is_definite (v, o) -> + WideningTokens.with_local_side_tokens (fun () -> + Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr + ) + | Addr (v, o) -> + WideningTokens.with_local_side_tokens (fun () -> + let s = ctx.ask MustLockset in + LockDomain.MustLockset.fold (fun ml st -> + if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then + st + else + let addr = Addr.Addr (LockDomain.MustLock.to_mval ml) in + Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr + ) s st + ) + | _ -> ctx.local + end | Events.Escape escaped -> Priv.escape ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st escaped | Events.EnterMultiThreaded -> diff --git a/tests/regression/13-privatized/93-unlock-idx-ambiguous.t b/tests/regression/13-privatized/93-unlock-idx-ambiguous.t index f954ac1d4c..8f24d728bf 100644 --- a/tests/regression/13-privatized/93-unlock-idx-ambiguous.t +++ b/tests/regression/13-privatized/93-unlock-idx-ambiguous.t @@ -1,8 +1,6 @@ -TODO: should not succeed - $ goblint --set ana.base.privatization protection --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) - [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Warning][Assert] Assertion "g == 0" is unknown. (93-unlock-idx-ambiguous.c:24:3-24:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -13,11 +11,9 @@ TODO: should not succeed unsafe: 0 total memory locations: 1 -TODO: should not succeed - $ goblint --set ana.base.privatization mutex-meet --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) - [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Warning][Assert] Assertion "g == 0" is unknown. (93-unlock-idx-ambiguous.c:24:3-24:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -28,11 +24,9 @@ TODO: should not succeed unsafe: 0 total memory locations: 1 -TODO: should not succeed - $ goblint --set ana.base.privatization lock --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) - [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Warning][Assert] Assertion "g == 0" is unknown. (93-unlock-idx-ambiguous.c:24:3-24:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -43,11 +37,9 @@ TODO: should not succeed unsafe: 0 total memory locations: 1 -TODO: should not succeed - $ goblint --set ana.base.privatization write --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) - [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Warning][Assert] Assertion "g == 0" is unknown. (93-unlock-idx-ambiguous.c:24:3-24:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -58,11 +50,9 @@ TODO: should not succeed unsafe: 0 total memory locations: 1 -TODO: should not succeed - $ goblint --set ana.base.privatization mine-nothread --enable ana.sv-comp.functions 93-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (93-unlock-idx-ambiguous.c:14:3-14:30) - [Success][Assert] Assertion "g == 0" will succeed (93-unlock-idx-ambiguous.c:24:3-24:26) + [Warning][Assert] Assertion "g == 0" is unknown. (93-unlock-idx-ambiguous.c:24:3-24:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 From b5bdf56373ac0ffd02728e1e55d7a512241c9562 Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Sun, 2 Jun 2024 18:26:57 +0200 Subject: [PATCH 163/566] int intervals: allow narrowing from widening thresholds --- src/cdomain/value/cdomains/intDomain.ml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 3bf81b1ba1..f482842ca8 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -572,6 +572,16 @@ module IntervalArith (Ints_t : IntOps.IntOps) = struct let min_ik' = Ints_t.to_bigint min_ik in let t = List.find_opt (fun x -> Z.compare l x >= 0 && Z.compare x min_ik' >= 0) ts in BatOption.map_default Ints_t.of_bigint min_ik t + let is_upper_threshold u = + let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in + let u = Ints_t.to_bigint u in + let t = List.find_opt (fun x -> Z.compare u x = 0) ts in + Option.is_some t + let is_lower_threshold l = + let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in + let l = Ints_t.to_bigint l in + let t = List.find_opt (fun x -> Z.compare l x = 0) ts in + Option.is_some t end module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = @@ -702,9 +712,10 @@ struct match x, y with | _,None | None, _ -> None | Some (x1,x2), Some (y1,y2) -> + let threshold = get_interval_threshold_widening () in let (min_ik, max_ik) = range ik in - let lr = if Ints_t.compare min_ik x1 = 0 then y1 else x1 in - let ur = if Ints_t.compare max_ik x2 = 0 then y2 else x2 in + let lr = if Ints_t.compare min_ik x1 = 0 || threshold && IArith.is_lower_threshold x1 then y1 else x1 in + let ur = if Ints_t.compare max_ik x2 = 0 || threshold && IArith.is_upper_threshold x2 then y2 else x2 in norm ik @@ Some (lr,ur) |> fst From 87d9a3b5c2b9848105805e08a2e30bb566c40595 Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Tue, 4 Jun 2024 13:03:59 +0200 Subject: [PATCH 164/566] add test for narrowing from thresholds --- tests/regression/03-practical/33-threshold-narrowing.c | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/regression/03-practical/33-threshold-narrowing.c diff --git a/tests/regression/03-practical/33-threshold-narrowing.c b/tests/regression/03-practical/33-threshold-narrowing.c new file mode 100644 index 0000000000..0c713fa273 --- /dev/null +++ b/tests/regression/03-practical/33-threshold-narrowing.c @@ -0,0 +1,8 @@ +// PARAM: --enable ana.int.interval --enable ana.int.interval_threshold_widening --set ana.int.interval_threshold_widening_constants comparisons +#include + +int main() { + int i; + for(i = 0; i < 10 && i < 20; i += 3); + __goblint_check(i <= 12); +} From ee6a4fb363337cfeb3da593f9c1e0f3e1bc552bd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 15:10:30 +0300 Subject: [PATCH 165/566] Add unsound test for unlocking non-definite mutex in relational privatizations --- .../46-apron2/87-unlock-idx-ambiguous.c | 28 +++++++++++++ .../46-apron2/87-unlock-idx-ambiguous.t | 39 +++++++++++++++++++ tests/regression/46-apron2/dune | 3 ++ 3 files changed, 70 insertions(+) create mode 100644 tests/regression/46-apron2/87-unlock-idx-ambiguous.c create mode 100644 tests/regression/46-apron2/87-unlock-idx-ambiguous.t diff --git a/tests/regression/46-apron2/87-unlock-idx-ambiguous.c b/tests/regression/46-apron2/87-unlock-idx-ambiguous.c new file mode 100644 index 0000000000..97cada5369 --- /dev/null +++ b/tests/regression/46-apron2/87-unlock-idx-ambiguous.c @@ -0,0 +1,28 @@ +// PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag +// TODO: why nonterm without threadflag path-sens? +#include +#include +extern _Bool __VERIFIER_nondet_bool(); + +int g; +pthread_mutex_t m[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[0]); + pthread_mutex_lock(&m[1]); // so we're unlocking a mutex we definitely hold + g++; + int r = __VERIFIER_nondet_bool(); + pthread_mutex_unlock(&m[r]); // TODO NOWARN (definitely held either way) + // could have unlocked m[0], so should have published g there + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m[0]); + __goblint_check(g == 0); // UNKNOWN! + pthread_mutex_unlock(&m[0]); + return 0; +} diff --git a/tests/regression/46-apron2/87-unlock-idx-ambiguous.t b/tests/regression/46-apron2/87-unlock-idx-ambiguous.t new file mode 100644 index 0000000000..5ff63babdc --- /dev/null +++ b/tests/regression/46-apron2/87-unlock-idx-ambiguous.t @@ -0,0 +1,39 @@ + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) + [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) + [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid-cluster12 --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c + [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) + [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + diff --git a/tests/regression/46-apron2/dune b/tests/regression/46-apron2/dune index cc0c04185c..f88a15d505 100644 --- a/tests/regression/46-apron2/dune +++ b/tests/regression/46-apron2/dune @@ -8,3 +8,6 @@ (glob_files ??-*.c)) (locks /update_suite) (action (chdir ../../.. (run %{update_suite} group apron2 -q)))) + +(cram + (deps (glob_files *.c))) From 9d32c70dbe8f336293b09da1550666203fd5ee45 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 15:18:34 +0300 Subject: [PATCH 166/566] Port base privatization non-definite mutex unlock unsoundness fix to relation --- src/analyses/apron/relationAnalysis.apron.ml | 34 +++++++++++++++---- .../46-apron2/87-unlock-idx-ambiguous.t | 6 ++-- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 6c118dac7a..0431d19a3e 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -685,13 +685,35 @@ struct let st = ctx.local in match e with | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *) - Priv.lock (Analyses.ask_of_ctx ctx) ctx.global st addr + begin match addr with + | UnknownPtr -> ctx.local + | Addr (v, _) when ctx.ask (IsMultiple v) -> ctx.local + | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> + Priv.lock (Analyses.ask_of_ctx ctx) ctx.global st addr + | _ -> ctx.local + end | Events.Unlock addr when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *) - if addr = UnknownPtr then - M.info ~category:Unsound "Unknown mutex unlocked, relation privatization unsound"; (* TODO: something more sound *) - WideningTokens.with_local_side_tokens (fun () -> - Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr - ) + begin match addr with + | UnknownPtr -> + M.info ~category:Unsound "Unknown mutex unlocked, relation privatization unsound"; (* TODO: something more sound *) + ctx.local (* TODO: remove all! *) + | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> + WideningTokens.with_local_side_tokens (fun () -> + Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr + ) + | Addr (v, o) -> + WideningTokens.with_local_side_tokens (fun () -> + let s = ctx.ask MustLockset in + LockDomain.MustLockset.fold (fun ml st -> + if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then + st + else + let addr = LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml) in + Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr + ) s st + ) + | _ -> ctx.local + end | Events.EnterMultiThreaded -> Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st | Events.Escape escaped -> diff --git a/tests/regression/46-apron2/87-unlock-idx-ambiguous.t b/tests/regression/46-apron2/87-unlock-idx-ambiguous.t index 5ff63babdc..abbdd74f00 100644 --- a/tests/regression/46-apron2/87-unlock-idx-ambiguous.t +++ b/tests/regression/46-apron2/87-unlock-idx-ambiguous.t @@ -1,6 +1,6 @@ $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) - [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Warning][Assert] Assertion "g == 0" is unknown. (87-unlock-idx-ambiguous.c:25:3-25:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -13,7 +13,7 @@ $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) - [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Warning][Assert] Assertion "g == 0" is unknown. (87-unlock-idx-ambiguous.c:25:3-25:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 @@ -26,7 +26,7 @@ $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid-cluster12 --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 87-unlock-idx-ambiguous.c [Warning][Unknown] unlocking mutex (m[def_exc:Unknown int([0,1])]) which may not be held (87-unlock-idx-ambiguous.c:15:3-15:30) - [Success][Assert] Assertion "g == 0" will succeed (87-unlock-idx-ambiguous.c:25:3-25:26) + [Warning][Assert] Assertion "g == 0" is unknown. (87-unlock-idx-ambiguous.c:25:3-25:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 From 4caae9403f38049989689becb026137248c6c1de Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 15:42:26 +0300 Subject: [PATCH 167/566] Extract CommonPriv.lift_lock and lift_unlock --- src/analyses/apron/relationAnalysis.apron.ml | 41 ++++++-------------- src/analyses/base.ml | 36 ++++------------- src/analyses/commonPriv.ml | 27 +++++++++++++ 3 files changed, 46 insertions(+), 58 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 0431d19a3e..4a6b266c25 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -682,38 +682,19 @@ struct ctx.local let event ctx e octx = + let ask = Analyses.ask_of_ctx ctx in let st = ctx.local in match e with - | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *) - begin match addr with - | UnknownPtr -> ctx.local - | Addr (v, _) when ctx.ask (IsMultiple v) -> ctx.local - | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> - Priv.lock (Analyses.ask_of_ctx ctx) ctx.global st addr - | _ -> ctx.local - end - | Events.Unlock addr when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* TODO: is this condition sound? *) - begin match addr with - | UnknownPtr -> - M.info ~category:Unsound "Unknown mutex unlocked, relation privatization unsound"; (* TODO: something more sound *) - ctx.local (* TODO: remove all! *) - | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> - WideningTokens.with_local_side_tokens (fun () -> - Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr - ) - | Addr (v, o) -> - WideningTokens.with_local_side_tokens (fun () -> - let s = ctx.ask MustLockset in - LockDomain.MustLockset.fold (fun ml st -> - if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then - st - else - let addr = LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml) in - Priv.unlock (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st addr - ) s st - ) - | _ -> ctx.local - end + | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) + CommonPriv.lift_lock ask (fun st m -> + Priv.lock ask ctx.global st m + ) st addr + | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) + WideningTokens.with_local_side_tokens (fun () -> + CommonPriv.lift_unlock ask (fun st m -> + Priv.unlock ask ctx.global ctx.sideg st m + ) st addr + ) | Events.EnterMultiThreaded -> Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st | Events.Escape escaped -> diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 13cf9f20da..fe4dc3e7de 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -3062,35 +3062,15 @@ struct match e with | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) if M.tracing then M.tracel "priv" "LOCK EVENT %a" LockDomain.Addr.pretty addr; - begin match addr with - | UnknownPtr -> ctx.local - | Addr (v, _) when ctx.ask (IsMultiple v) -> ctx.local - | Addr (v, o) when Addr.Mval.is_definite (v, o) -> - Priv.lock ask (priv_getg ctx.global) st addr - | _ -> ctx.local - end + CommonPriv.lift_lock ask (fun st m -> + Priv.lock ask (priv_getg ctx.global) st m + ) st addr | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) - begin match addr with - | UnknownPtr -> - M.info ~category:Unsound "Unknown mutex unlocked, base privatization unsound"; (* TODO: something more sound *) - ctx.local (* TODO: remove all! *) - | Addr (v, o) when Addr.Mval.is_definite (v, o) -> - WideningTokens.with_local_side_tokens (fun () -> - Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr - ) - | Addr (v, o) -> - WideningTokens.with_local_side_tokens (fun () -> - let s = ctx.ask MustLockset in - LockDomain.MustLockset.fold (fun ml st -> - if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then - st - else - let addr = Addr.Addr (LockDomain.MustLock.to_mval ml) in - Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st addr - ) s st - ) - | _ -> ctx.local - end + WideningTokens.with_local_side_tokens (fun () -> + CommonPriv.lift_unlock ask (fun st m -> + Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st m + ) st addr + ) | Events.Escape escaped -> Priv.escape ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st escaped | Events.EnterMultiThreaded -> diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index aa829c4606..02ad65d8b6 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -305,3 +305,30 @@ struct let startstate () = W.bot (), LMust.top (), L.bot () end + + +let lift_lock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = + match addr with + | UnknownPtr -> st + | Addr (v, _) when ask.f (IsMultiple v) -> st + | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> + f st addr + | _ -> st + +let lift_unlock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = + match addr with + | UnknownPtr -> + M.info ~category:Unsound "Unknown mutex unlocked, privatization unsound"; (* TODO: something more sound *) + st (* TODO: remove all! *) + | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> + f st addr + | Addr (v, o) -> + let s = ask.f MustLockset in + LockDomain.MustLockset.fold (fun ml st -> + if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then + st + else + let addr = LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml) in + f st addr + ) s st + | _ -> st From ba3c020eb69b44ff7e1221f40103064c40ef3d0a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 15:54:35 +0300 Subject: [PATCH 168/566] Refactor CommonPriv.lift_lock and lift_unlock --- src/analyses/commonPriv.ml | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 02ad65d8b6..127dfe0383 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -308,27 +308,36 @@ end let lift_lock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = + (* Should be in sync with: + 1. LocksetAnalysis.MakeMust.event + 2. MutexAnalysis.Spec.Arg.add + 3. LockDomain.MustLocksetRW.add_mval_rw *) match addr with | UnknownPtr -> st | Addr (v, _) when ask.f (IsMultiple v) -> st - | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> - f st addr - | _ -> st + | Addr mv when LockDomain.Mval.is_definite mv -> f st addr + | Addr _ + | NullPtr + | StrPtr _ -> st let lift_unlock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = + (* Should be in sync with: + 1. LocksetAnalysis.MakeMust.event + 2. MutexAnalysis.Spec.Arg.remove + 3. MutexAnalysis.Spec.Arg.remove_all + 4. LockDomain.MustLocksetRW.remove_mval_rw *) match addr with | UnknownPtr -> M.info ~category:Unsound "Unknown mutex unlocked, privatization unsound"; (* TODO: something more sound *) st (* TODO: remove all! *) - | Addr (v, o) when LockDomain.Mval.is_definite (v, o) -> - f st addr - | Addr (v, o) -> - let s = ask.f MustLockset in + | StrPtr _ + | NullPtr -> st + | Addr mv when LockDomain.Mval.is_definite mv -> f st addr + | Addr mv -> LockDomain.MustLockset.fold (fun ml st -> - if LockDomain.MustLock.semantic_equal_mval ml (v, o) = Some false then + if LockDomain.MustLock.semantic_equal_mval ml mv = Some false then st else - let addr = LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml) in - f st addr - ) s st - | _ -> st + (* call privatization's unlock only with definite lock *) + f st (Addr (LockDomain.MustLock.to_mval ml)) (* TODO: no conversion *) + ) (ask.f MustLockset) st From 3dcf0c1c05173a5cb8dd7b4a7feb5e4235d4c542 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 16:00:46 +0300 Subject: [PATCH 169/566] Add unsound test for unlocking unknown mutex in privatizations --- .../13-privatized/94-unlock-unknown.c | 25 +++++++ .../13-privatized/94-unlock-unknown.t | 75 +++++++++++++++++++ .../regression/46-apron2/88-unlock-unknown.c | 25 +++++++ .../regression/46-apron2/88-unlock-unknown.t | 45 +++++++++++ 4 files changed, 170 insertions(+) create mode 100644 tests/regression/13-privatized/94-unlock-unknown.c create mode 100644 tests/regression/13-privatized/94-unlock-unknown.t create mode 100644 tests/regression/46-apron2/88-unlock-unknown.c create mode 100644 tests/regression/46-apron2/88-unlock-unknown.t diff --git a/tests/regression/13-privatized/94-unlock-unknown.c b/tests/regression/13-privatized/94-unlock-unknown.c new file mode 100644 index 0000000000..0897c38de8 --- /dev/null +++ b/tests/regression/13-privatized/94-unlock-unknown.c @@ -0,0 +1,25 @@ +// PARAM: --set ana.base.privatization protection --enable ana.sv-comp.functions +#include +#include + +int g; +pthread_mutex_t m[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[0]); + g++; + pthread_mutex_t *r; // rand + pthread_mutex_unlock(r); + // could have unlocked m[0], so should have published g there + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m[0]); + __goblint_check(g == 0); // UNKNOWN! + pthread_mutex_unlock(&m[0]); + return 0; +} diff --git a/tests/regression/13-privatized/94-unlock-unknown.t b/tests/regression/13-privatized/94-unlock-unknown.t new file mode 100644 index 0000000000..2d4e0d97cc --- /dev/null +++ b/tests/regression/13-privatized/94-unlock-unknown.t @@ -0,0 +1,75 @@ + $ goblint --set ana.base.privatization protection --enable ana.sv-comp.functions 94-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.base.privatization mutex-meet --enable ana.sv-comp.functions 94-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.base.privatization lock --enable ana.sv-comp.functions 94-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.base.privatization write --enable ana.sv-comp.functions 94-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.base.privatization mine-nothread --enable ana.sv-comp.functions 94-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + diff --git a/tests/regression/46-apron2/88-unlock-unknown.c b/tests/regression/46-apron2/88-unlock-unknown.c new file mode 100644 index 0000000000..3e09ee8659 --- /dev/null +++ b/tests/regression/46-apron2/88-unlock-unknown.c @@ -0,0 +1,25 @@ +// PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions +#include +#include + +int g; +pthread_mutex_t m[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER}; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[0]); + g++; + pthread_mutex_t *r; // rand + pthread_mutex_unlock(r); + // could have unlocked m[0], so should have published g there + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m[0]); + __goblint_check(g == 0); // UNKNOWN! + pthread_mutex_unlock(&m[0]); + return 0; +} diff --git a/tests/regression/46-apron2/88-unlock-unknown.t b/tests/regression/46-apron2/88-unlock-unknown.t new file mode 100644 index 0000000000..f6649a116e --- /dev/null +++ b/tests/regression/46-apron2/88-unlock-unknown.t @@ -0,0 +1,45 @@ + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions 88-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 88-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid-cluster12 --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 88-unlock-unknown.c + [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) + [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) + [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + From fade1823f0f44e3e20cbb61522b276a4c538feb6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 16:02:51 +0300 Subject: [PATCH 170/566] Fix privatization unlocking unknown mutex unsoundness --- src/analyses/commonPriv.ml | 6 ++++-- .../regression/13-privatized/94-unlock-unknown.t | 15 +++++---------- tests/regression/46-apron2/88-unlock-unknown.t | 9 +++------ 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 127dfe0383..fb5d0db142 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -328,8 +328,10 @@ let lift_unlock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = 4. LockDomain.MustLocksetRW.remove_mval_rw *) match addr with | UnknownPtr -> - M.info ~category:Unsound "Unknown mutex unlocked, privatization unsound"; (* TODO: something more sound *) - st (* TODO: remove all! *) + LockDomain.MustLockset.fold (fun ml st -> + (* call privatization's unlock only with definite lock *) + f st (LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml)) (* TODO: no conversion *) + ) (ask.f MustLockset) st | StrPtr _ | NullPtr -> st | Addr mv when LockDomain.Mval.is_definite mv -> f st addr diff --git a/tests/regression/13-privatized/94-unlock-unknown.t b/tests/regression/13-privatized/94-unlock-unknown.t index 2d4e0d97cc..cc0aa921fe 100644 --- a/tests/regression/13-privatized/94-unlock-unknown.t +++ b/tests/regression/13-privatized/94-unlock-unknown.t @@ -1,8 +1,7 @@ $ goblint --set ana.base.privatization protection --enable ana.sv-comp.functions 94-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (94-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -14,10 +13,9 @@ total memory locations: 1 $ goblint --set ana.base.privatization mutex-meet --enable ana.sv-comp.functions 94-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (94-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -29,10 +27,9 @@ total memory locations: 1 $ goblint --set ana.base.privatization lock --enable ana.sv-comp.functions 94-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (94-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -44,10 +41,9 @@ total memory locations: 1 $ goblint --set ana.base.privatization write --enable ana.sv-comp.functions 94-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (94-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -59,10 +55,9 @@ total memory locations: 1 $ goblint --set ana.base.privatization mine-nothread --enable ana.sv-comp.functions 94-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (94-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (94-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (94-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (94-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 diff --git a/tests/regression/46-apron2/88-unlock-unknown.t b/tests/regression/46-apron2/88-unlock-unknown.t index f6649a116e..35e4ec4503 100644 --- a/tests/regression/46-apron2/88-unlock-unknown.t +++ b/tests/regression/46-apron2/88-unlock-unknown.t @@ -1,8 +1,7 @@ $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions 88-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (88-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -14,10 +13,9 @@ total memory locations: 1 $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 88-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (88-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 @@ -29,10 +27,9 @@ total memory locations: 1 $ goblint --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-tid-cluster12 --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag 88-unlock-unknown.c - [Info][Unsound] Unknown mutex unlocked, privatization unsound (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking unknown mutex which may not be held (88-unlock-unknown.c:12:3-12:26) [Warning][Unknown] unlocking NULL mutex (88-unlock-unknown.c:12:3-12:26) - [Success][Assert] Assertion "g == 0" will succeed (88-unlock-unknown.c:22:3-22:26) + [Warning][Assert] Assertion "g == 0" is unknown. (88-unlock-unknown.c:22:3-22:26) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 From aeae2a97d2d475bba55ed2f0b8af94d497e7ab1c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 16:37:20 +0300 Subject: [PATCH 171/566] Refactor privatizations' lock and unlock mutex type to Mustlock --- src/analyses/apron/relationPriv.apron.ml | 28 +++++++------- src/analyses/basePriv.ml | 48 ++++++++++++------------ src/analyses/basePriv.mli | 4 +- src/analyses/commonPriv.ml | 18 ++++----- src/cdomains/lockDomain.ml | 2 + 5 files changed, 51 insertions(+), 49 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index cada5ff888..1302fa3743 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -33,8 +33,8 @@ module type S = the state when following conditional guards. *) val write_global: ?invariant:bool -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> varinfo -> varinfo -> relation_components_t - val lock: Q.ask -> (V.t -> G.t) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t - val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t + val lock: Q.ask -> (V.t -> G.t) -> relation_components_t -> LockDomain.MustLock.t -> relation_components_t + val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> LockDomain.MustLock.t -> relation_components_t val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `Return | `Init | `Thread] -> relation_components_t @@ -471,7 +471,7 @@ struct let startstate () = () - let atomic_mutex = LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var + let atomic_mutex = LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var let get_m_with_mutex_inits ask getg m = let get_m = getg (V.mutex m) in @@ -577,9 +577,9 @@ struct let write_escape = write_global_internal ~skip_meet:true let lock ask getg (st: relation_components_t) m = - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in + let atomic = Param.handle_atomic && LockDomain.MustLock.equal m atomic_mutex in (* TODO: somehow actually unneeded here? *) - if not atomic && Locksets.(not (MustLockset.mem_addr m (current_lockset ask))) then ( + if not atomic && Locksets.(not (MustLockset.mem m (current_lockset ask))) then ( let rel = st.rel in let get_m = get_m_with_mutex_inits ask getg m in (* Additionally filter get_m in case it contains variables it no longer protects. E.g. in 36/22. *) @@ -592,7 +592,7 @@ struct st (* sound w.r.t. recursive lock *) let unlock ask getg sideg (st: relation_components_t) m: relation_components_t = - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in + let atomic = Param.handle_atomic && LockDomain.MustLock.equal m atomic_mutex in let rel = st.rel in if not atomic then ( let rel_side = keep_only_protected_globals ask m rel in @@ -703,7 +703,7 @@ module type ClusterArg = functor (RD: RelationDomain.RD) -> sig module LRD: Lattice.S - val keep_only_protected_globals: Q.ask -> LockDomain.Addr.t -> LRD.t -> LRD.t + val keep_only_protected_globals: Q.ask -> LockDomain.MustLock.t -> LRD.t -> LRD.t val keep_global: varinfo -> LRD.t -> LRD.t val lock: RD.t -> LRD.t -> LRD.t -> RD.t @@ -962,7 +962,7 @@ struct let get_m_with_mutex_inits inits ask getg m = let get_m = get_relevant_writes ask m (G.mutex @@ getg (V.mutex m)) in - if M.tracing then M.traceli "relationpriv" "get_m_with_mutex_inits %a\n get=%a" LockDomain.Addr.pretty m LRD.pretty get_m; + if M.tracing then M.traceli "relationpriv" "get_m_with_mutex_inits %a\n get=%a" LockDomain.MustLock.pretty m LRD.pretty get_m; let r = if not inits then get_m @@ -975,7 +975,7 @@ struct if M.tracing then M.traceu "relationpriv" "-> %a" LRD.pretty r; r - let atomic_mutex = LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var + let atomic_mutex = LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var let get_mutex_global_g_with_mutex_inits inits ask getg g = let get_mutex_global_g = @@ -1088,8 +1088,8 @@ struct {rel = rel_local; priv = (W.add g w,lmust,l)} (* Keep write local as if it were protected by the atomic section. *) let lock ask getg (st: relation_components_t) m = - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in - if not atomic && Locksets.(not (MustLockset.mem_addr m (current_lockset ask))) then ( + let atomic = Param.handle_atomic && LockDomain.MustLock.equal m atomic_mutex in + if not atomic && Locksets.(not (MustLockset.mem m (current_lockset ask))) then ( let rel = st.rel in let _,lmust,l = st.priv in let lm = LLock.mutex m in @@ -1112,7 +1112,7 @@ struct RD.keep_filter oct protected let unlock ask getg sideg (st: relation_components_t) m: relation_components_t = - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in + let atomic = Param.handle_atomic && LockDomain.MustLock.equal m atomic_mutex in let rel = st.rel in let w,lmust,l = st.priv in if not atomic then ( @@ -1290,7 +1290,7 @@ struct r let lock ask getg st m = - if M.tracing then M.traceli "relationpriv" "lock %a" LockDomain.Addr.pretty m; + if M.tracing then M.traceli "relationpriv" "lock %a" LockDomain.MustLock.pretty m; if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in @@ -1302,7 +1302,7 @@ struct r let unlock ask getg sideg st m = - if M.tracing then M.traceli "relationpriv" "unlock %a" LockDomain.Addr.pretty m; + if M.tracing then M.traceli "relationpriv" "unlock %a" LockDomain.MustLock.pretty m; if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index f874232176..9dbffe40b1 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -28,8 +28,8 @@ sig * the state when following conditional guards. *) val write_global: ?invariant:bool -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> varinfo -> VD.t -> BaseComponents (D).t - val lock: Q.ask -> (V.t -> G.t) -> BaseComponents (D).t -> LockDomain.Addr.t -> BaseComponents (D).t - val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> LockDomain.Addr.t -> BaseComponents (D).t + val lock: Q.ask -> (V.t -> G.t) -> BaseComponents (D).t -> LockDomain.MustLock.t -> BaseComponents (D).t + val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> LockDomain.MustLock.t -> BaseComponents (D).t val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> [`Normal | `Join | `Return | `Init | `Thread] -> BaseComponents (D).t @@ -191,7 +191,7 @@ struct let get_mutex_inits = getg V.mutex_inits in let is_in_Gm x _ = is_protected_by ask m x in let get_mutex_inits' = CPA.filter is_in_Gm get_mutex_inits in - if M.tracing then M.tracel "priv" "get_m_with_mutex_inits %a:\n get_m: %a\n get_mutex_inits: %a\n get_mutex_inits': %a" LockDomain.Addr.pretty m CPA.pretty get_m CPA.pretty get_mutex_inits CPA.pretty get_mutex_inits'; + if M.tracing then M.tracel "priv" "get_m_with_mutex_inits %a:\n get_m: %a\n get_mutex_inits: %a\n get_mutex_inits': %a" LockDomain.MustLock.pretty m CPA.pretty get_m CPA.pretty get_mutex_inits CPA.pretty get_mutex_inits'; CPA.join get_m get_mutex_inits' (** [get_m_with_mutex_inits] optimized for implementation-specialized [read_global]. *) @@ -281,7 +281,7 @@ struct cpa' *) let lock ask getg (st: BaseComponents (D).t) m = - if Locksets.(not (MustLockset.mem_addr m (current_lockset ask))) then ( + if Locksets.(not (MustLockset.mem m (current_lockset ask))) then ( let get_m = get_m_with_mutex_inits ask getg m in (* Really we want is_unprotected, but pthread_cond_wait emits unlock-lock events, where our (necessary) original context still has the mutex, @@ -292,7 +292,7 @@ struct No other privatization uses is_unprotected, so this hack is only needed here. *) let is_in_V x _ = is_protected_by ask m x && is_unprotected_without ask x m in let cpa' = CPA.filter is_in_V get_m in - if M.tracing then M.tracel "priv" "PerMutexOplusPriv.lock m=%a cpa'=%a" LockDomain.Addr.pretty m CPA.pretty cpa'; + if M.tracing then M.tracel "priv" "PerMutexOplusPriv.lock m=%a cpa'=%a" LockDomain.MustLock.pretty m CPA.pretty cpa'; {st with cpa = CPA.fold CPA.add cpa' st.cpa} ) else @@ -301,7 +301,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let is_in_Gm x _ = is_protected_by ask m x in let side_m_cpa = CPA.filter is_in_Gm st.cpa in - if M.tracing then M.tracel "priv" "PerMutexOplusPriv.unlock m=%a side_m_cpa=%a" LockDomain.Addr.pretty m CPA.pretty side_m_cpa; + if M.tracing then M.tracel "priv" "PerMutexOplusPriv.unlock m=%a side_m_cpa=%a" LockDomain.MustLock.pretty m CPA.pretty side_m_cpa; sideg (V.mutex m) side_m_cpa; st @@ -377,14 +377,14 @@ struct cpa' *) let lock (ask: Queries.ask) getg (st: BaseComponents (D).t) m = - if Locksets.(not (MustLockset.mem_addr m (current_lockset ask))) then ( + if Locksets.(not (MustLockset.mem m (current_lockset ask))) then ( let get_m = get_m_with_mutex_inits ask getg m in (* Additionally filter get_m in case it contains variables it no longer protects. *) let is_in_Gm x _ = is_protected_by ask m x in let get_m = CPA.filter is_in_Gm get_m in let long_meet m1 m2 = CPA.long_map2 VD.meet m1 m2 in let meet = long_meet st.cpa get_m in - if M.tracing then M.tracel "priv" "LOCK %a:\n get_m: %a\n meet: %a" LockDomain.Addr.pretty m CPA.pretty get_m CPA.pretty meet; + if M.tracing then M.tracel "priv" "LOCK %a:\n get_m: %a\n meet: %a" LockDomain.MustLock.pretty m CPA.pretty get_m CPA.pretty meet; {st with cpa = meet} ) else @@ -523,7 +523,7 @@ struct {st with cpa = cpa'; priv = (W.add x w,LMust.add lm lmust,l')} let lock (ask: Queries.ask) getg (st: BaseComponents (D).t) m = - if Locksets.(not (MustLockset.mem_addr m (current_lockset ask))) then ( + if Locksets.(not (MustLockset.mem m (current_lockset ask))) then ( let _,lmust,l = st.priv in let lm = LLock.mutex m in let get_m = get_m_with_mutex_inits (not (LMust.mem lm lmust)) ask getg m in @@ -750,7 +750,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let sideg = Wrapper.sideg ask sideg in - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) in + let atomic = Param.handle_atomic && LockDomain.MustLock.equal m (LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var) in (* TODO: what about G_m globals in cpa that weren't actually written? *) CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_protected_by ask m x then ( (* is_in_Gm *) @@ -1016,13 +1016,13 @@ struct {st with cpa = cpa'} let unlock ask getg sideg (st: BaseComponents (D).t) m = - let s = MustLockset.remove_addr m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let t = current_thread ask in let side_cpa = CPA.filter (fun x _ -> GWeak.fold (fun s' tm acc -> (* TODO: swap 2^M and T partitioning for lookup by t here first? *) let v = ThreadMap.find t tm in - (MustLockset.mem_addr m s' && not (VD.is_bot v)) || acc + (MustLockset.mem m s' && not (VD.is_bot v)) || acc ) (G.weak (getg (V.global x))) false ) st.cpa in @@ -1074,10 +1074,10 @@ struct {st with cpa = cpa'} let unlock ask getg sideg (st: BaseComponents (D).t) m = - let s = MustLockset.remove_addr m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let side_cpa = CPA.filter (fun x _ -> GWeak.fold (fun s' v acc -> - (MustLockset.mem_addr m s' && not (VD.is_bot v)) || acc + (MustLockset.mem m s' && not (VD.is_bot v)) || acc ) (G.weak (getg (V.global x))) false ) st.cpa in @@ -1149,7 +1149,7 @@ struct {st with cpa = cpa'} let unlock ask getg sideg (st: BaseComponents (D).t) m = - let s = MustLockset.remove_addr m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let is_in_W x _ = W.mem x st.priv in let side_cpa = CPA.filter is_in_W st.cpa in sideg (V.mutex m) (G.create_sync (GSync.singleton s side_cpa)); @@ -1192,13 +1192,13 @@ struct module DV = struct - include MapDomain.MapBot_LiftTop (Lock) (MustVars) + include MapDomain.MapBot_LiftTop (LockDomain.MustLock) (MustVars) let name () = "V" end module L = struct - include MapDomain.MapBot_LiftTop (Lock) (MinLocksets) + include MapDomain.MapBot_LiftTop (LockDomain.MustLock) (MinLocksets) let name () = "L" end end @@ -1301,7 +1301,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let sideg = Wrapper.sideg ask sideg in let getg = Wrapper.getg ask getg in - let s = MustLockset.remove_addr m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let is_in_G x _ = is_global ask x in let side_cpa = CPA.filter is_in_G st.cpa in let side_cpa = CPA.mapi (fun x v -> @@ -1471,10 +1471,10 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let getg = Wrapper.getg ask getg in let sideg = Wrapper.sideg ask sideg in - let s = MustLockset.remove_addr m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let (w, p) = st.priv in let p' = P.map (fun s' -> MinLocksets.add s s') p in - if M.tracing then M.traceli "priv" "unlock %a %a" Lock.pretty m CPA.pretty st.cpa; + if M.tracing then M.traceli "priv" "unlock %a %a" LockDomain.MustLock.pretty m CPA.pretty st.cpa; let side_gsyncw = CPA.fold (fun x v acc -> if is_global ask x then ( let w_x = W.find x w in @@ -1487,7 +1487,7 @@ struct acc ) st.cpa (GSyncW.bot ()) in - if M.tracing then M.traceu "priv" "unlock %a %a" Lock.pretty m GSyncW.pretty side_gsyncw; + if M.tracing then M.traceu "priv" "unlock %a %a" LockDomain.MustLock.pretty m GSyncW.pretty side_gsyncw; sideg (V.mutex m) (UnwrappedG.create_sync (GSync.singleton s side_gsyncw)); {st with priv = (w, p')} @@ -1656,7 +1656,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let getg = Wrapper.getg ask getg in let sideg = Wrapper.sideg ask sideg in - let s = MustLockset.remove_addr m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let ((w, p), vl) = st.priv in let p' = P.map (fun s' -> MinLocksets.add s s') p in let side_gsyncw = CPA.fold (fun x v acc -> @@ -1809,7 +1809,7 @@ struct r let lock ask getg st m = - if M.tracing then M.traceli "priv" "lock %a" LockDomain.Addr.pretty m; + if M.tracing then M.traceli "priv" "lock %a" LockDomain.MustLock.pretty m; if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let getg x = let r = getg x in @@ -1821,7 +1821,7 @@ struct r let unlock ask getg sideg st m = - if M.tracing then M.traceli "priv" "unlock %a" LockDomain.Addr.pretty m; + if M.tracing then M.traceli "priv" "unlock %a" LockDomain.MustLock.pretty m; if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let getg x = let r = getg x in diff --git a/src/analyses/basePriv.mli b/src/analyses/basePriv.mli index e176a450fa..03a40405c0 100644 --- a/src/analyses/basePriv.mli +++ b/src/analyses/basePriv.mli @@ -17,8 +17,8 @@ sig val read_global: Queries.ask -> (V.t -> G.t) -> BaseDomain.BaseComponents (D).t -> varinfo -> BaseDomain.VD.t val write_global: ?invariant:bool -> Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> varinfo -> BaseDomain.VD.t -> BaseDomain.BaseComponents (D).t - val lock: Queries.ask -> (V.t -> G.t) -> BaseDomain.BaseComponents (D).t -> LockDomain.Addr.t -> BaseDomain.BaseComponents (D).t - val unlock: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> LockDomain.Addr.t -> BaseDomain.BaseComponents (D).t + val lock: Queries.ask -> (V.t -> G.t) -> BaseDomain.BaseComponents (D).t -> LockDomain.MustLock.t -> BaseDomain.BaseComponents (D).t + val unlock: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> LockDomain.MustLock.t -> BaseDomain.BaseComponents (D).t val sync: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> [`Normal | `Join | `Return | `Init | `Thread] -> BaseDomain.BaseComponents (D).t diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index fe0611236c..5de56a8e59 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -74,12 +74,12 @@ struct let is_unprotected_without ask ?(write=true) ?(protection=Strong) x m: bool = (if protection = Weak then ThreadFlag.is_currently_multi ask else ThreadFlag.has_ever_been_multi ask) && - ask.f (Q.MayBePublicWithout {global=x; write; without_mutex=m; protection}) + ask.f (Q.MayBePublicWithout {global=x; write; without_mutex=Addr (LockDomain.MustLock.to_mval m); protection}) (* TODO: no mutex conversion? *) let is_protected_by ask ?(protection=Strong) m x: bool = is_global ask x && not (VD.is_immediate_type x.vtype) && - ask.f (Q.MustBeProtectedBy {mutex=m; global=x; write=true; protection}) + ask.f (Q.MustBeProtectedBy {mutex=Addr (LockDomain.MustLock.to_mval m); global=x; write=true; protection}) (* TODO: no mutex conversion? *) let protected_vars (ask: Q.ask): varinfo list = LockDomain.MustLockset.fold (fun ml acc -> @@ -92,7 +92,7 @@ module MutexGlobals = struct module VMutex = struct - include LockDomain.Addr + include LockDomain.MustLock let name () = "mutex" end module VMutexInits = Printable.UnitConf (struct let name = "MUTEX_INITS" end) @@ -106,7 +106,7 @@ struct include Printable.Either3Conf (struct include Printable.DefaultConf let expand2 = false end) (VMutex) (VMutexInits) (VGlobal) let name () = "MutexGlobals" let mutex x: t = `Left x - let mutex_mustlock x = mutex (Addr (LockDomain.MustLock.to_mval x)) + let mutex_mustlock x = mutex x (* TODO: remove *) let mutex_inits: t = `Middle () let global x: t = `Right x end @@ -250,7 +250,7 @@ struct module LLock = struct - include Printable.Either (Locksets.Lock) (struct include CilType.Varinfo let name () = "global" end) + include Printable.Either (LockDomain.MustLock) (struct include CilType.Varinfo let name () = "global" end) let mutex m = `Left m let global x = `Right x end @@ -313,7 +313,7 @@ let lift_lock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = match addr with | UnknownPtr -> st | Addr (v, _) when ask.f (IsMultiple v) -> st - | Addr mv when LockDomain.Mval.is_definite mv -> f st addr + | Addr mv when LockDomain.Mval.is_definite mv -> f st (LockDomain.MustLock.of_mval mv) | Addr _ | NullPtr | StrPtr _ -> st @@ -328,16 +328,16 @@ let lift_unlock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = | UnknownPtr -> LockDomain.MustLockset.fold (fun ml st -> (* call privatization's unlock only with definite lock *) - f st (LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml)) (* TODO: no conversion *) + f st ml ) (ask.f MustLockset) st | StrPtr _ | NullPtr -> st - | Addr mv when LockDomain.Mval.is_definite mv -> f st addr + | Addr mv when LockDomain.Mval.is_definite mv -> f st (LockDomain.MustLock.of_mval mv) | Addr mv -> LockDomain.MustLockset.fold (fun ml st -> if LockDomain.MustLock.semantic_equal_mval ml mv = Some false then st else (* call privatization's unlock only with definite lock *) - f st (Addr (LockDomain.MustLock.to_mval ml)) (* TODO: no conversion *) + f st ml ) (ask.f MustLockset) st diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 1c9fcb98c7..be22ec1aea 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -24,6 +24,8 @@ struct else Some false + let of_var v: t = (v, `NoOffset) + let of_mval ((v, o): Mval.t): t = (v, Offset.Poly.map_indices (fun i -> IndexDomain.to_int i |> Option.get) o) From 500b45236ecd10ccb85b60c87d3eecea40d30821 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 16:38:33 +0300 Subject: [PATCH 172/566] Remove now-unnecessary must lockset functions --- src/analyses/basePriv.ml | 6 +++--- src/analyses/commonPriv.ml | 7 ------- src/cdomains/lockDomain.ml | 16 ---------------- 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 9dbffe40b1..6deec0e8ca 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -911,7 +911,7 @@ struct MustLockset.fold (fun m acc -> GSync.fold (fun s' cpa' acc -> SyncRange.fold_sync_vars VS.add cpa' acc - ) (G.sync (getg (V.mutex_mustlock m))) acc + ) (G.sync (getg (V.mutex m))) acc ) s VS.empty |> VS.elements end @@ -1410,7 +1410,7 @@ struct let d_cpa = CPA.find x st.cpa in let d_sync = MustLockset.fold (fun m acc -> if MinLocksets.exists (fun s''' -> not (MustLockset.mem m s''')) p_x then - let syncs = UnwrappedG.sync (getg (V.mutex_mustlock m)) in + let syncs = UnwrappedG.sync (getg (V.mutex m)) in GSync.fold (fun s' gsyncw' acc -> if MustLockset.disjoint s s' then GSyncW.fold (fun w' cpa' acc -> @@ -1600,7 +1600,7 @@ struct let d_m = VD.join d_m_sync d_m_weak in let d_g_sync = MustLockset.fold (fun m acc -> if MinLocksets.exists (fun s''' -> not (MustLockset.mem m s''')) p_x then - let syncs = UnwrappedG.sync (getg (V.mutex_mustlock m)) in + let syncs = UnwrappedG.sync (getg (V.mutex m)) in GSync.fold (fun s' gsyncw' acc -> if MustLockset.disjoint s s' then GSyncW.fold (fun w' cpa' acc -> diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 5de56a8e59..257fe9a038 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -106,7 +106,6 @@ struct include Printable.Either3Conf (struct include Printable.DefaultConf let expand2 = false end) (VMutex) (VMutexInits) (VGlobal) let name () = "MutexGlobals" let mutex x: t = `Left x - let mutex_mustlock x = mutex x (* TODO: remove *) let mutex_inits: t = `Middle () let global x: t = `Right x end @@ -131,12 +130,6 @@ end module Locksets = struct - module Lock = - struct - include LockDomain.Addr - let name () = "lock" - end - module MustLockset = LockDomain.MustLockset let current_lockset (ask: Q.ask): MustLockset.t = diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index be22ec1aea..08353f4795 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -37,22 +37,6 @@ module MustLockset = struct include SetDomain.Reverse (SetDomain.ToppedSet (MustLock) (struct let topname = "All mutexes" end)) - let mem_addr (a: Addr.t) (set: t) = (* TODO: replace with mem_mval *) - match Addr.to_mval a with - | Some mv when Mval.is_definite mv -> - let ml = MustLock.of_mval mv in - mem ml set - | _ -> - false - - let remove_addr (a: Addr.t) (set: t) = (* TODO: replace with remove_mval *) - match Addr.to_mval a with - | Some mv when Mval.is_definite mv -> - let ml = MustLock.of_mval mv in - remove ml set - | _ -> - set - let all (): t = `Top let is_all (set: t) = set = `Top end From 90fc5aa1af4b0b734289917c3f713b5bf326a408 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 16:51:16 +0300 Subject: [PATCH 173/566] Refine MustBeProtectedBy and MayBePublicWithout query mutex type --- src/analyses/commonPriv.ml | 4 ++-- src/analyses/mutexAnalysis.ml | 5 ++--- src/domains/queries.ml | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 257fe9a038..3673991fda 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -74,12 +74,12 @@ struct let is_unprotected_without ask ?(write=true) ?(protection=Strong) x m: bool = (if protection = Weak then ThreadFlag.is_currently_multi ask else ThreadFlag.has_ever_been_multi ask) && - ask.f (Q.MayBePublicWithout {global=x; write; without_mutex=Addr (LockDomain.MustLock.to_mval m); protection}) (* TODO: no mutex conversion? *) + ask.f (Q.MayBePublicWithout {global=x; write; without_mutex=m; protection}) let is_protected_by ask ?(protection=Strong) m x: bool = is_global ask x && not (VD.is_immediate_type x.vtype) && - ask.f (Q.MustBeProtectedBy {mutex=Addr (LockDomain.MustLock.to_mval m); global=x; write=true; protection}) (* TODO: no mutex conversion? *) + ask.f (Q.MustBeProtectedBy {mutex=m; global=x; write=true; protection}) let protected_vars (ask: Q.ask): varinfo list = LockDomain.MustLockset.fold (fun ml acc -> diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index b44795b6da..9b6aa4f4ca 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -209,15 +209,14 @@ struct MustLockset.disjoint held_locks protecting | Queries.MayBePublicWithout _ when MustLocksetRW.is_all ls -> false | Queries.MayBePublicWithout {global=v; write; without_mutex; protection} -> - let held_locks = MustLocksetRW.to_must_lockset @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in + let held_locks = MustLockset.remove without_mutex (MustLocksetRW.to_must_lockset ls) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if Mutexes.mem verifier_atomic (Lockset.export_locks (Lockset.remove (without_mutex, true) ctx.local)) then false else *) MustLockset.disjoint held_locks protecting - | Queries.MustBeProtectedBy {mutex = Addr mutex_mv; global=v; write; protection} when Mval.is_definite mutex_mv -> (* only definite Addrs can be in must-locksets to begin with, anything else cannot protect anything *) - let ml = LockDomain.MustLock.of_mval mutex_mv in + | Queries.MustBeProtectedBy {mutex = ml; global=v; write; protection} -> let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then diff --git a/src/domains/queries.ml b/src/domains/queries.ml index a904f696eb..edb5c44c56 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -49,8 +49,8 @@ end (* Helper definitions for deriving complex parts of Any.compare below. *) type maybepublic = {global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash] -type maybepublicwithout = {global: CilType.Varinfo.t; write: bool; without_mutex: PreValueDomain.Addr.t; protection: Protection.t} [@@deriving ord, hash] -type mustbeprotectedby = {mutex: PreValueDomain.Addr.t; global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash] +type maybepublicwithout = {global: CilType.Varinfo.t; write: bool; without_mutex: LockDomain.MustLock.t; protection: Protection.t} [@@deriving ord, hash] +type mustbeprotectedby = {mutex: LockDomain.MustLock.t; global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash] type mustprotectedvars = {mutex: LockDomain.MustLock.t; write: bool} [@@deriving ord, hash] type access = | Memory of {exp: CilType.Exp.t; var_opt: CilType.Varinfo.t option; kind: AccessKind.t} (** Memory location access (race). *) From 11526a831b47d0e408b4d56a2b0015f3e8c29978 Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Tue, 4 Jun 2024 20:23:16 +0200 Subject: [PATCH 174/566] fix: narrow only when interval shrinks --- src/cdomain/value/cdomains/intDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index f482842ca8..b882df732b 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -714,8 +714,8 @@ struct | Some (x1,x2), Some (y1,y2) -> let threshold = get_interval_threshold_widening () in let (min_ik, max_ik) = range ik in - let lr = if Ints_t.compare min_ik x1 = 0 || threshold && IArith.is_lower_threshold x1 then y1 else x1 in - let ur = if Ints_t.compare max_ik x2 = 0 || threshold && IArith.is_upper_threshold x2 then y2 else x2 in + let lr = if Ints_t.compare min_ik x1 = 0 || threshold && Ints_t.compare y1 x1 > 0 && IArith.is_lower_threshold x1 then y1 else x1 in + let ur = if Ints_t.compare max_ik x2 = 0 || threshold && Ints_t.compare y2 x2 < 0 && IArith.is_upper_threshold x2 then y2 else x2 in norm ik @@ Some (lr,ur) |> fst From d6456c87b6a206bc73116a82d385fbfe48431fff Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Tue, 4 Jun 2024 20:32:37 +0200 Subject: [PATCH 175/566] narrow thresholds for int interval sets --- src/cdomain/value/cdomains/intDomain.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index b882df732b..7f91c9d036 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -1393,8 +1393,9 @@ struct let min_ys = minimal ys |> Option.get in let max_ys = maximal ys |> Option.get in let min_range,max_range = range ik in - let min = if min_xs =. min_range then min_ys else min_xs in - let max = if max_xs =. max_range then max_ys else max_xs in + let threshold = get_interval_threshold_widening () in + let min = if min_xs =. min_range || threshold && min_ys <. min_xs && IArith.is_lower_threshold min_xs then min_ys else min_xs in + let max = if max_xs =. max_range || threshold && max_ys <. max_xs && IArith.is_upper_threshold max_xs then max_ys else max_xs in xs |> (function (_, y)::z -> (min, y)::z | _ -> []) |> List.rev From 2e42c7bf1c664d07ab5844246338547f4d28f3c8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Jun 2024 12:36:10 +0300 Subject: [PATCH 176/566] Add ghost_variable and ghost_update YAML entry types to option --- conf/svcomp-ghost.json | 4 +++- src/analyses/mutexGhosts.ml | 3 +++ src/config/options.schema.json | 4 +++- src/witness/witnessGhost.ml | 3 +++ tests/regression/13-privatized/04-priv_multi.t | 4 ++-- tests/regression/13-privatized/25-struct_nr.t | 2 +- tests/regression/13-privatized/74-mutex.t | 6 +++--- tests/regression/13-privatized/92-idx_priv.t | 2 +- tests/regression/29-svcomp/16-atomic_priv.t | 4 ++-- tests/regression/36-apron/12-traces-min-rpb1.t | 2 +- tests/regression/56-witness/64-ghost-multiple-protecting.t | 6 +++--- tests/regression/56-witness/65-ghost-ambiguous-lock.t | 2 +- tests/regression/56-witness/66-ghost-alloc-lock.t | 2 +- tests/regression/56-witness/67-ghost-no-unlock.t | 2 +- tests/regression/56-witness/68-ghost-ambiguous-idx.t | 2 +- 15 files changed, 29 insertions(+), 19 deletions(-) diff --git a/conf/svcomp-ghost.json b/conf/svcomp-ghost.json index 229dd9ef46..84f127eb91 100644 --- a/conf/svcomp-ghost.json +++ b/conf/svcomp-ghost.json @@ -116,7 +116,9 @@ "enabled": true, "format-version": "0.1", "entry-types": [ - "flow_insensitive_invariant" + "flow_insensitive_invariant", + "ghost_variable", + "ghost_update" ] }, "invariant": { diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 159498cfb1..6e95504731 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -96,6 +96,9 @@ struct | Locked _ -> false | Multithreaded -> true + let ghost_var_available ctx v = + WitnessGhost.enabled () && ghost_var_available ctx v + let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | GhostVarAvailable v -> ghost_var_available ctx v diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 4a77aae5f0..779b9bbf65 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -2580,7 +2580,9 @@ "precondition_loop_invariant", "loop_invariant_certificate", "precondition_loop_invariant_certificate", - "invariant_set" + "invariant_set", + "ghost_variable", + "ghost_update" ] }, "default": [ diff --git a/src/witness/witnessGhost.ml b/src/witness/witnessGhost.ml index cdd26b36aa..91d513ceae 100644 --- a/src/witness/witnessGhost.ml +++ b/src/witness/witnessGhost.ml @@ -1,5 +1,8 @@ (** Ghost variables for YAML witnesses. *) +let enabled () = + YamlWitness.entry_type_enabled YamlWitnessType.GhostVariable.entry_type && YamlWitness.entry_type_enabled YamlWitnessType.GhostUpdate.entry_type + module Var = WitnessGhostVar include Var diff --git a/tests/regression/13-privatized/04-priv_multi.t b/tests/regression/13-privatized/04-priv_multi.t index 952696a5c4..576c89ad4d 100644 --- a/tests/regression/13-privatized/04-priv_multi.t +++ b/tests/regression/13-privatized/04-priv_multi.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 04-priv_multi.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 04-priv_multi.c [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) [Warning][Deadcode] Function 'dispose' has dead code: @@ -174,7 +174,7 @@ Flow-insensitive invariants as location invariants. - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' --enable witness.invariant.flow_insensitive-as-location 04-priv_multi.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --enable witness.invariant.flow_insensitive-as-location 04-priv_multi.c [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) [Warning][Deadcode] Function 'dispose' has dead code: diff --git a/tests/regression/13-privatized/25-struct_nr.t b/tests/regression/13-privatized/25-struct_nr.t index f3ebcd1c52..342cfaf99c 100644 --- a/tests/regression/13-privatized/25-struct_nr.t +++ b/tests/regression/13-privatized/25-struct_nr.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 25-struct_nr.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 25-struct_nr.c [Success][Assert] Assertion "glob1 == 5" will succeed (25-struct_nr.c:26:3-26:30) [Success][Assert] Assertion "t == 5" will succeed (25-struct_nr.c:16:3-16:26) [Success][Assert] Assertion "glob1 == -10" will succeed (25-struct_nr.c:18:3-18:32) diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index a00f49eb1a..f6f2fa8463 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -1,4 +1,4 @@ - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) @@ -84,7 +84,7 @@ Flow-insensitive invariants as location invariants. - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' --enable witness.invariant.flow_insensitive-as-location 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --enable witness.invariant.flow_insensitive-as-location 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) @@ -150,7 +150,7 @@ Earlyglobs shouldn't cause protected writes in multithreaded mode from being imm Same with mutex-meet. - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) diff --git a/tests/regression/13-privatized/92-idx_priv.t b/tests/regression/13-privatized/92-idx_priv.t index 64a3309009..bd6db7e2ef 100644 --- a/tests/regression/13-privatized/92-idx_priv.t +++ b/tests/regression/13-privatized/92-idx_priv.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 92-idx_priv.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 92-idx_priv.c [Success][Assert] Assertion "data == 0" will succeed (92-idx_priv.c:22:3-22:29) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 diff --git a/tests/regression/29-svcomp/16-atomic_priv.t b/tests/regression/29-svcomp/16-atomic_priv.t index b10265d4e8..83bc201a6c 100644 --- a/tests/regression/29-svcomp/16-atomic_priv.t +++ b/tests/regression/29-svcomp/16-atomic_priv.t @@ -1,4 +1,4 @@ - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection-atomic --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 16-atomic_priv.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection-atomic --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 16-atomic_priv.c [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:12:3-12:33) [Success][Assert] Assertion "myglobal == 6" will succeed (16-atomic_priv.c:14:3-14:33) [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:16:3-16:33) @@ -43,7 +43,7 @@ Non-atomic privatization: - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 16-atomic_priv.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 16-atomic_priv.c [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:12:3-12:33) [Success][Assert] Assertion "myglobal == 6" will succeed (16-atomic_priv.c:14:3-14:33) [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:16:3-16:33) diff --git a/tests/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t index 7aca1dea0b..1c3253afbc 100644 --- a/tests/regression/36-apron/12-traces-min-rpb1.t +++ b/tests/regression/36-apron/12-traces-min-rpb1.t @@ -1,4 +1,4 @@ - $ goblint --enable warn.deterministic --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 12-traces-min-rpb1.c --enable ana.apron.invariant.diff-box + $ goblint --enable warn.deterministic --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 12-traces-min-rpb1.c --enable ana.apron.invariant.diff-box [Warning][Assert] Assertion "g == h" is unknown. (12-traces-min-rpb1.c:27:3-27:26) [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:16:3-16:26) [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:29:3-29:26) diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.t b/tests/regression/56-witness/64-ghost-multiple-protecting.t index 53323355c5..943934a7be 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.t +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 @@ -144,7 +144,7 @@ protection doesn't have precise protected invariant for g2. type: assertion format: C - $ goblint --set ana.base.privatization protection-read --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization protection-read --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 @@ -295,7 +295,7 @@ protection-read has precise protected invariant for g2. type: assertion format: C - $ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 diff --git a/tests/regression/56-witness/65-ghost-ambiguous-lock.t b/tests/regression/56-witness/65-ghost-ambiguous-lock.t index 708e27ca64..d7d57d8a00 100644 --- a/tests/regression/56-witness/65-ghost-ambiguous-lock.t +++ b/tests/regression/56-witness/65-ghost-ambiguous-lock.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 65-ghost-ambiguous-lock.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 65-ghost-ambiguous-lock.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 23 dead: 0 diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.t b/tests/regression/56-witness/66-ghost-alloc-lock.t index e4d128b71e..e4268ec1cb 100644 --- a/tests/regression/56-witness/66-ghost-alloc-lock.t +++ b/tests/regression/56-witness/66-ghost-alloc-lock.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set ana.malloc.unique_address_count 1 --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 66-ghost-alloc-lock.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set ana.malloc.unique_address_count 1 --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 66-ghost-alloc-lock.c [Success][Assert] Assertion "g1 == 0" will succeed (66-ghost-alloc-lock.c:31:3-31:27) [Success][Assert] Assertion "g2 == 0" will succeed (66-ghost-alloc-lock.c:34:3-34:27) [Info][Deadcode] Logical lines of code (LLoC) summary: diff --git a/tests/regression/56-witness/67-ghost-no-unlock.t b/tests/regression/56-witness/67-ghost-no-unlock.t index 491dd9cf44..aed0ac3414 100644 --- a/tests/regression/56-witness/67-ghost-no-unlock.t +++ b/tests/regression/56-witness/67-ghost-no-unlock.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 67-ghost-no-unlock.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 67-ghost-no-unlock.c [Success][Assert] Assertion "g1 == 0" will succeed (67-ghost-no-unlock.c:24:3-24:27) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 diff --git a/tests/regression/56-witness/68-ghost-ambiguous-idx.t b/tests/regression/56-witness/68-ghost-ambiguous-idx.t index 48837fcabb..9f50ab7429 100644 --- a/tests/regression/56-witness/68-ghost-ambiguous-idx.t +++ b/tests/regression/56-witness/68-ghost-ambiguous-idx.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant"]' 68-ghost-ambiguous-idx.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 68-ghost-ambiguous-idx.c [Warning][Assert] Assertion "data == 0" is unknown. (68-ghost-ambiguous-idx.c:24:3-24:29) [Warning][Unknown] unlocking mutex (m[4]) which may not be held (68-ghost-ambiguous-idx.c:25:3-25:30) [Info][Deadcode] Logical lines of code (LLoC) summary: From e7931ffe4afc2be4872b15d94a4ab3aa4ed66453 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Jun 2024 12:53:02 +0300 Subject: [PATCH 177/566] Make InvariantGlobalNodes query lazy in YAML witness generation --- 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 b7bf11a31c..fd8f4b5249 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -325,7 +325,7 @@ struct (* Generate flow-insensitive invariants *) let entries = if entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type then ( - let ns = R.ask_global InvariantGlobalNodes in + let ns = lazy (R.ask_global InvariantGlobalNodes) in GHT.fold (fun g v acc -> match g with | `Left g -> (* Spec global *) @@ -352,7 +352,7 @@ struct entry :: acc ) acc invs | None -> acc - ) ns acc + ) (Lazy.force ns) acc | `Bot, _ | `Top, _ -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) acc end From 36ff6217074eb8c25a2528979ebc634d3cba789e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Jun 2024 13:46:04 +0300 Subject: [PATCH 178/566] Fix comment about YamlEntryGlobal --- 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 fd8f4b5249..596a35f631 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -364,7 +364,7 @@ struct entries in - (* Generate flow-insensitive invariants *) + (* Generate flow-insensitive entries (ghost variables and ghost updates) *) let entries = if true then ( GHT.fold (fun g v acc -> From 1e37e0b3a20c976ec89de5cd49618a3c70e5e58b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Jun 2024 14:10:38 +0300 Subject: [PATCH 179/566] Use GobOption.exists instead of Option.is_some && Option.get --- src/cdomain/value/cdomains/valueDomain.ml | 15 ++++++++------- src/solver/generic.ml | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index de64fde807..be089f0ca5 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -978,8 +978,9 @@ struct let blob_size_opt = ID.to_int s in not @@ ask.is_multiple var && not @@ Cil.isVoidType t (* Size of value is known *) - && Option.is_some blob_size_opt (* Size of blob is known *) - && Z.equal (Option.get blob_size_opt) (Z.of_int @@ Cil.bitsSizeOf (TComp (toptype, []))/8) + && GobOption.exists (fun blob_size -> (* Size of blob is known *) + Z.equal blob_size (Z.of_int @@ Cil.bitsSizeOf (TComp (toptype, []))/8) + ) blob_size_opt | _ -> false in if do_strong_update then @@ -997,11 +998,11 @@ struct | (Var var, _) -> let blob_size_opt = ID.to_int s in not @@ ask.is_multiple var - && Option.is_some blob_size_opt (* Size of blob is known *) - && (( - not @@ Cil.isVoidType t (* Size of value is known *) - && Z.equal (Option.get blob_size_opt) (Z.of_int @@ Cil.alignOf_int t) - ) || blob_destructive) + && GobOption.exists (fun blob_size -> (* Size of blob is known *) + (not @@ Cil.isVoidType t (* Size of value is known *) + && Z.equal blob_size (Z.of_int @@ Cil.alignOf_int t)) + || blob_destructive + ) blob_size_opt | _ -> false end in diff --git a/src/solver/generic.ml b/src/solver/generic.ml index 1f0df57843..737aba6762 100644 --- a/src/solver/generic.ml +++ b/src/solver/generic.ml @@ -44,7 +44,7 @@ struct let histo = HM.create 1024 let increase (v:Var.t) = let set v c = - if not full_trace && (c > start_c && c > !max_c && (Option.is_none !max_var || not (Var.equal (Option.get !max_var) v))) then begin + if not full_trace && (c > start_c && c > !max_c && not (GobOption.exists (Var.equal v) !max_var)) then begin if tracing then trace "sol" "Switched tracing to %a" Var.pretty_trace v; max_c := c; max_var := Some v @@ -75,7 +75,7 @@ struct let update_var_event x o n = if tracing then increase x; - if full_trace || ((not (Dom.is_bot o)) && Option.is_some !max_var && Var.equal (Option.get !max_var) x) then begin + if full_trace || (not (Dom.is_bot o) && GobOption.exists (Var.equal x) !max_var) then begin if tracing then tracei "sol_max" "(%d) Update to %a" !max_c Var.pretty_trace x; if tracing then traceu "sol_max" "%a" Dom.pretty_diff (n, o) end From 67af68adb8834cf446cd24bdf41325db4de91b18 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Jun 2024 14:11:13 +0300 Subject: [PATCH 180/566] Remove redundant match guard in condVars If is None, then body handles it anyway with Option.map and defaults to d. --- src/analyses/condVars.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 22b9db1cd4..448e3a79e5 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -116,7 +116,7 @@ struct match rval with | BinOp (op, _, _, _) when is_cmp op -> (* logical expression *) save_expr lval rval - | Lval k when Option.is_some (mustPointTo ctx (AddrOf k) >? flip D.get d) -> (* var-eq for transitive closure *) + | Lval k -> (* var-eq for transitive closure *) mustPointTo ctx (AddrOf k) >? flip D.get_elt d |> Option.map (save_expr lval) |? d | _ -> d From 3f8f8bf129dd26fc005422a3f43a07c5dfcc2b4b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Jun 2024 14:12:03 +0300 Subject: [PATCH 181/566] Avoid intermediate lists in AddressSet.string_writing_defined Just check existance on set directly. --- src/cdomain/value/cdomains/addressDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomain/value/cdomains/addressDomain.ml b/src/cdomain/value/cdomains/addressDomain.ml index dc1ebfff7d..da684cc4f4 100644 --- a/src/cdomain/value/cdomains/addressDomain.ml +++ b/src/cdomain/value/cdomains/addressDomain.ml @@ -320,7 +320,7 @@ struct let string_writing_defined dest = (* if the destination address set contains a StrPtr, writing to such a string literal is undefined behavior *) - if List.exists Option.is_some (List.map Addr.to_c_string (elements dest)) then + if exists (fun a -> Option.is_some (Addr.to_c_string a)) dest then (M.warn ~category:M.Category.Behavior.Undefined.other "May write to a string literal, which leads to a segmentation fault in most cases"; false) else From ad73869770c75b3aa7ed144c605079d86026ee36 Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Wed, 5 Jun 2024 13:51:10 +0200 Subject: [PATCH 182/566] integrate comments --- src/cdomain/value/cdomains/intDomain.ml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 7f91c9d036..a67740e9db 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -575,13 +575,11 @@ module IntervalArith (Ints_t : IntOps.IntOps) = struct let is_upper_threshold u = let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in let u = Ints_t.to_bigint u in - let t = List.find_opt (fun x -> Z.compare u x = 0) ts in - Option.is_some t + List.exists (fun x -> Z.compare u x = 0) ts let is_lower_threshold l = let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in let l = Ints_t.to_bigint l in - let t = List.find_opt (fun x -> Z.compare l x = 0) ts in - Option.is_some t + List.exists (fun x -> Z.compare l x = 0) ts end module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = @@ -1394,7 +1392,7 @@ struct let max_ys = maximal ys |> Option.get in let min_range,max_range = range ik in let threshold = get_interval_threshold_widening () in - let min = if min_xs =. min_range || threshold && min_ys <. min_xs && IArith.is_lower_threshold min_xs then min_ys else min_xs in + let min = if min_xs =. min_range || threshold && min_ys >. min_xs && IArith.is_lower_threshold min_xs then min_ys else min_xs in let max = if max_xs =. max_range || threshold && max_ys <. max_xs && IArith.is_upper_threshold max_xs then max_ys else max_xs in xs |> (function (_, y)::z -> (min, y)::z | _ -> []) From 94a31d4ef61f6e4227c0329f96342ae1f471eeb5 Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Wed, 5 Jun 2024 14:53:00 +0200 Subject: [PATCH 183/566] test interval-set narrowing and lower bounds --- ...rrowing.c => 33-threshold-narrowing-intervals.c} | 5 +++++ .../34-threshold-narrowing-interval-sets.c | 13 +++++++++++++ 2 files changed, 18 insertions(+) rename tests/regression/03-practical/{33-threshold-narrowing.c => 33-threshold-narrowing-intervals.c} (74%) create mode 100644 tests/regression/03-practical/34-threshold-narrowing-interval-sets.c diff --git a/tests/regression/03-practical/33-threshold-narrowing.c b/tests/regression/03-practical/33-threshold-narrowing-intervals.c similarity index 74% rename from tests/regression/03-practical/33-threshold-narrowing.c rename to tests/regression/03-practical/33-threshold-narrowing-intervals.c index 0c713fa273..ef38c151da 100644 --- a/tests/regression/03-practical/33-threshold-narrowing.c +++ b/tests/regression/03-practical/33-threshold-narrowing-intervals.c @@ -5,4 +5,9 @@ int main() { int i; for(i = 0; i < 10 && i < 20; i += 3); __goblint_check(i <= 12); + + int j; + for(j = 0; j > -10 && j > -20; j-= 3); + __goblint_check(j >= -12); + } diff --git a/tests/regression/03-practical/34-threshold-narrowing-interval-sets.c b/tests/regression/03-practical/34-threshold-narrowing-interval-sets.c new file mode 100644 index 0000000000..c1f11c091e --- /dev/null +++ b/tests/regression/03-practical/34-threshold-narrowing-interval-sets.c @@ -0,0 +1,13 @@ +// PARAM: --enable ana.int.interval_set --enable ana.int.interval_threshold_widening --set ana.int.interval_threshold_widening_constants comparisons +#include + +int main() { + int i; + for(i = 0; i < 10 && i < 20; i += 3); + __goblint_check(i <= 12); + + int j; + for(j = 0; j > -10 && j > -20; j-= 3); + __goblint_check(j >= -12); + +} From 8aa9c4f4f0be045e0ba0363dd12bc72790907a27 Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Wed, 5 Jun 2024 14:54:16 +0200 Subject: [PATCH 184/566] add unit test for interval(-set) narrowing --- tests/unit/cdomains/intDomainTest.ml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/unit/cdomains/intDomainTest.ml b/tests/unit/cdomains/intDomainTest.ml index e697b022eb..a60b7a6cb1 100644 --- a/tests/unit/cdomains/intDomainTest.ml +++ b/tests/unit/cdomains/intDomainTest.ml @@ -205,6 +205,7 @@ struct let i65536 = I.of_interval ik (Z.zero, of_int 65536) let i65537 = I.of_interval ik (Z.zero, of_int 65537) let imax = I.of_interval ik (Z.zero, of_int 2147483647) + let imin = I.of_interval ik (of_int (-2147483648), Z.zero) let assert_equal x y = assert_equal ~cmp:I.equal ~printer:I.show x y @@ -218,9 +219,34 @@ struct assert_equal imax (I.widen ik i65536 i65537); assert_equal imax (I.widen ik i65536 imax) + let test_interval_narrow _ = + GobConfig.set_bool "ana.int.interval_threshold_widening" true; + GobConfig.set_string "ana.int.interval_threshold_widening_constants" "comparisons"; + let i_zero_one = I.of_interval ik (Z.zero, Z.one) in + let i_zero_five = I.of_interval ik (Z.zero, of_int 5) in + let to_widen = I.of_interval ik (Z.zero, Z.zero) in + (* this should widen to [0, x], where x is the next largest threshold above 5 or the maximal int*) + let widened = I.widen ik to_widen i_zero_five in + (* either way, narrowing from [0, x] to [0, 1] should be possible *) + let narrowed = I.narrow ik widened i_zero_one in + (* however, narrowing should not allow [0, x] to grow *) + let narrowed2 = I.narrow ik widened imax in + assert_equal i_zero_one narrowed; + assert_equal widened narrowed2; + + (* the same tests, but for lower bounds *) + let i_minus_one_zero = I.of_interval ik (Z.minus_one, Z.zero) in + let i_minus_five_zero = I.of_interval ik (of_int (-5), Z.zero) in + let widened = I.widen ik to_widen i_minus_five_zero in + let narrowed = I.narrow ik widened i_minus_one_zero in + let narrowed2 = I.narrow ik widened imin in + assert_equal i_minus_one_zero narrowed; + assert_equal widened narrowed2 + let test () = [ "test_interval_rem" >:: test_interval_rem; "test_interval_widen" >:: test_interval_widen; + "test_interval_narrow" >:: test_interval_narrow; ] end From a384886cd8424cbdada153072647a68c703c789b Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 5 Jun 2024 18:58:45 +0300 Subject: [PATCH 185/566] Make `to_float` return float instead float option --- src/cdomain/value/cdomains/floatDomain.ml | 4 ++-- src/common/cdomains/floatOps/floatOps.ml | 6 ++--- src/common/cdomains/floatOps/floatOps.mli | 2 +- tests/unit/cdomains/floatDomainTest.ml | 28 +++++++++++------------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/cdomain/value/cdomains/floatDomain.ml b/src/cdomain/value/cdomains/floatDomain.ml index c86770826c..0977c68890 100644 --- a/src/cdomain/value/cdomains/floatDomain.ml +++ b/src/cdomain/value/cdomains/floatDomain.ml @@ -241,12 +241,12 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let minimal = function | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "minimal %s" (show Bot))) - | Interval (l, _) -> Float_t.to_float l + | Interval (l, _) -> Some (Float_t.to_float l) | _ -> None let maximal = function | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "maximal %s" (show Bot))) - | Interval (_, h) -> Float_t.to_float h + | Interval (_, h) -> Some (Float_t.to_float h) | _ -> None let is_exact = function diff --git a/src/common/cdomains/floatOps/floatOps.ml b/src/common/cdomains/floatOps/floatOps.ml index 80946b1749..eced6af248 100644 --- a/src/common/cdomains/floatOps/floatOps.ml +++ b/src/common/cdomains/floatOps/floatOps.ml @@ -16,7 +16,7 @@ module type CFloatType = sig val pi : t val of_float: round_mode -> float -> t - val to_float: t -> float option + val to_float: t -> float val to_big_int: t -> Z.t val is_finite: t -> bool @@ -68,7 +68,7 @@ module CDouble = struct let pi = Float.pi let of_float _ x = x - let to_float x = Some x + let to_float x = x let to_big_int = big_int_of_float let is_finite = Float.is_finite @@ -112,7 +112,7 @@ module CFloat = struct let smallest = smallest' () let pi = pi' () - let to_float x = Some x + let to_float x = x let to_big_int = big_int_of_float let is_finite x = Float.is_finite x && x >= lower_bound && x <= upper_bound diff --git a/src/common/cdomains/floatOps/floatOps.mli b/src/common/cdomains/floatOps/floatOps.mli index 7cd74f7e7d..a07582f442 100644 --- a/src/common/cdomains/floatOps/floatOps.mli +++ b/src/common/cdomains/floatOps/floatOps.mli @@ -17,7 +17,7 @@ module type CFloatType = sig val pi : t val of_float: round_mode -> float -> t - val to_float: t -> float option + val to_float: t -> float val to_big_int: t -> Z.t val is_finite: t -> bool diff --git a/tests/unit/cdomains/floatDomainTest.ml b/tests/unit/cdomains/floatDomainTest.ml index 5f3cd4db47..c6cb18500a 100644 --- a/tests/unit/cdomains/floatDomainTest.ml +++ b/tests/unit/cdomains/floatDomainTest.ml @@ -14,12 +14,12 @@ struct let mul = Float_t.mul let div = Float_t.div - let pred x = Option.get (to_float (Float_t.pred (of_float Nearest x))) - let succ x = Option.get (to_float (Float_t.succ (of_float Nearest x))) + let pred x = to_float (Float_t.pred (of_float Nearest x)) + let succ x = to_float (Float_t.succ (of_float Nearest x)) - let fmax = Option.get (to_float Float_t.upper_bound) - let fmin = Option.get (to_float Float_t.lower_bound) - let fsmall = Option.get (to_float Float_t.smallest) + let fmax = to_float Float_t.upper_bound + let fmin = to_float Float_t.lower_bound + let fsmall = to_float Float_t.smallest let fi_zero = FI.of_const 0. let fi_one = FI.of_const 1. @@ -53,7 +53,7 @@ struct FI.top () + FI.top () = FI.top (); (FI.of_const fmin) + (FI.of_const fmax) = fi_zero; (FI.of_const fsmall) + (FI.of_const fsmall) = FI.of_const (fsmall +. fsmall); - let one_plus_fsmall = Option.get (to_float (Float_t.add Up (Float_t.of_float Up 1.) Float_t.smallest)) in + let one_plus_fsmall = to_float (Float_t.add Up (Float_t.of_float Up 1.) Float_t.smallest) in (FI.of_const fsmall) + (FI.of_const 1.) = FI.of_interval (1., one_plus_fsmall); (FI.of_interval (1., 2.)) + (FI.of_interval (2., 3.)) = FI.of_interval (3., 5.); (FI.of_interval (-. 2., 3.)) + (FI.of_interval (-. 100., 20.)) = FI.of_interval (-. 102., 23.); @@ -286,27 +286,27 @@ struct let test_FI_add = QCheck.Test.make ~name:"test_FI_add" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.add (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (Option.get (to_float (add Up (of_float Nearest arg1) (of_float Nearest arg2))))) result) && - (FI.leq (FI.of_const (Option.get (to_float (add Down (of_float Nearest arg1) (of_float Nearest arg2))))) result)) + (FI.leq (FI.of_const (to_float (add Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && + (FI.leq (FI.of_const (to_float (add Down (of_float Nearest arg1) (of_float Nearest arg2)))) result)) let test_FI_sub = QCheck.Test.make ~name:"test_FI_sub" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.sub (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (Option.get (to_float (sub Up (of_float Nearest arg1) (of_float Nearest arg2))))) result) && - (FI.leq (FI.of_const (Option.get (to_float (sub Down (of_float Nearest arg1) (of_float Nearest arg2))))) result)) + (FI.leq (FI.of_const (to_float (sub Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && + (FI.leq (FI.of_const (to_float (sub Down (of_float Nearest arg1) (of_float Nearest arg2))))) result) let test_FI_mul = QCheck.Test.make ~name:"test_FI_mul" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.mul (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (Option.get (to_float (mul Up (of_float Nearest arg1) (of_float Nearest arg2))))) result) && - (FI.leq (FI.of_const (Option.get (to_float (mul Down (of_float Nearest arg1) (of_float Nearest arg2))))) result)) + (FI.leq (FI.of_const (to_float (mul Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && + (FI.leq (FI.of_const (to_float (mul Down (of_float Nearest arg1) (of_float Nearest arg2)))) result)) let test_FI_div = QCheck.Test.make ~name:"test_FI_div" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.div (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (Option.get (to_float (div Up (of_float Nearest arg1) (of_float Nearest arg2))))) result) && - (FI.leq (FI.of_const (Option.get (to_float (div Down (of_float Nearest arg1) (of_float Nearest arg2))))) result)) + (FI.leq (FI.of_const (to_float (div Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && + (FI.leq (FI.of_const (to_float (div Down (of_float Nearest arg1) (of_float Nearest arg2)))) result)) let test () = [ From 1cbf7aca72bfbc8073a223f13723080b0d3c67ac Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 5 Jun 2024 19:13:38 +0300 Subject: [PATCH 186/566] Define CFloatType t = float --- src/common/cdomains/floatOps/floatOps.ml | 4 ++-- src/common/cdomains/floatOps/floatOps.mli | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/cdomains/floatOps/floatOps.ml b/src/common/cdomains/floatOps/floatOps.ml index eced6af248..002f382aff 100644 --- a/src/common/cdomains/floatOps/floatOps.ml +++ b/src/common/cdomains/floatOps/floatOps.ml @@ -6,7 +6,7 @@ type round_mode = | Down module type CFloatType = sig - type t + type t = float val name: string val zero: t @@ -97,7 +97,7 @@ module CDouble = struct external atof: round_mode -> string -> t = "atof_double" end -module CFloat = struct +module CFloat : CFloatType = struct type t = float [@@deriving eq, ord, hash, to_yojson] let name = "float" diff --git a/src/common/cdomains/floatOps/floatOps.mli b/src/common/cdomains/floatOps/floatOps.mli index a07582f442..f45785a7d5 100644 --- a/src/common/cdomains/floatOps/floatOps.mli +++ b/src/common/cdomains/floatOps/floatOps.mli @@ -7,7 +7,7 @@ type round_mode = | Down module type CFloatType = sig - type t + type t = float val name: string val zero: t From 87f60f3b16b25261b29f3e999cc7d53b90ade7da Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 5 Jun 2024 19:27:10 +0300 Subject: [PATCH 187/566] Remove identity function `to_float` --- src/cdomain/value/cdomains/floatDomain.ml | 4 ++-- src/common/cdomains/floatOps/floatOps.ml | 3 --- src/common/cdomains/floatOps/floatOps.mli | 1 - tests/unit/cdomains/floatDomainTest.ml | 29 +++++++++++------------ 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/cdomain/value/cdomains/floatDomain.ml b/src/cdomain/value/cdomains/floatDomain.ml index 0977c68890..db6e4eccb5 100644 --- a/src/cdomain/value/cdomains/floatDomain.ml +++ b/src/cdomain/value/cdomains/floatDomain.ml @@ -241,12 +241,12 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let minimal = function | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "minimal %s" (show Bot))) - | Interval (l, _) -> Some (Float_t.to_float l) + | Interval (l, _) -> Some l | _ -> None let maximal = function | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "maximal %s" (show Bot))) - | Interval (_, h) -> Some (Float_t.to_float h) + | Interval (_, h) -> Some h | _ -> None let is_exact = function diff --git a/src/common/cdomains/floatOps/floatOps.ml b/src/common/cdomains/floatOps/floatOps.ml index 002f382aff..6088fa9447 100644 --- a/src/common/cdomains/floatOps/floatOps.ml +++ b/src/common/cdomains/floatOps/floatOps.ml @@ -16,7 +16,6 @@ module type CFloatType = sig val pi : t val of_float: round_mode -> float -> t - val to_float: t -> float val to_big_int: t -> Z.t val is_finite: t -> bool @@ -68,7 +67,6 @@ module CDouble = struct let pi = Float.pi let of_float _ x = x - let to_float x = x let to_big_int = big_int_of_float let is_finite = Float.is_finite @@ -112,7 +110,6 @@ module CFloat : CFloatType = struct let smallest = smallest' () let pi = pi' () - let to_float x = x let to_big_int = big_int_of_float let is_finite x = Float.is_finite x && x >= lower_bound && x <= upper_bound diff --git a/src/common/cdomains/floatOps/floatOps.mli b/src/common/cdomains/floatOps/floatOps.mli index f45785a7d5..2deb55a764 100644 --- a/src/common/cdomains/floatOps/floatOps.mli +++ b/src/common/cdomains/floatOps/floatOps.mli @@ -17,7 +17,6 @@ module type CFloatType = sig val pi : t val of_float: round_mode -> float -> t - val to_float: t -> float val to_big_int: t -> Z.t val is_finite: t -> bool diff --git a/tests/unit/cdomains/floatDomainTest.ml b/tests/unit/cdomains/floatDomainTest.ml index c6cb18500a..6b49d21a1a 100644 --- a/tests/unit/cdomains/floatDomainTest.ml +++ b/tests/unit/cdomains/floatDomainTest.ml @@ -7,19 +7,18 @@ struct module FI = Domain_t module IT = IntDomain.IntDomTuple - let to_float = Float_t.to_float let of_float = Float_t.of_float let add = Float_t.add let sub = Float_t.sub let mul = Float_t.mul let div = Float_t.div - let pred x = to_float (Float_t.pred (of_float Nearest x)) - let succ x = to_float (Float_t.succ (of_float Nearest x)) + let pred x = Float_t.pred (of_float Nearest x) + let succ x = Float_t.succ (of_float Nearest x) - let fmax = to_float Float_t.upper_bound - let fmin = to_float Float_t.lower_bound - let fsmall = to_float Float_t.smallest + let fmax = Float_t.upper_bound + let fmin = Float_t.lower_bound + let fsmall = Float_t.smallest let fi_zero = FI.of_const 0. let fi_one = FI.of_const 1. @@ -53,7 +52,7 @@ struct FI.top () + FI.top () = FI.top (); (FI.of_const fmin) + (FI.of_const fmax) = fi_zero; (FI.of_const fsmall) + (FI.of_const fsmall) = FI.of_const (fsmall +. fsmall); - let one_plus_fsmall = to_float (Float_t.add Up (Float_t.of_float Up 1.) Float_t.smallest) in + let one_plus_fsmall = Float_t.add Up (Float_t.of_float Up 1.) Float_t.smallest in (FI.of_const fsmall) + (FI.of_const 1.) = FI.of_interval (1., one_plus_fsmall); (FI.of_interval (1., 2.)) + (FI.of_interval (2., 3.)) = FI.of_interval (3., 5.); (FI.of_interval (-. 2., 3.)) + (FI.of_interval (-. 100., 20.)) = FI.of_interval (-. 102., 23.); @@ -286,27 +285,27 @@ struct let test_FI_add = QCheck.Test.make ~name:"test_FI_add" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.add (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (to_float (add Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && - (FI.leq (FI.of_const (to_float (add Down (of_float Nearest arg1) (of_float Nearest arg2)))) result)) + (FI.leq (FI.of_const (add Up (of_float Nearest arg1) (of_float Nearest arg2))) result) && + (FI.leq (FI.of_const (add Down (of_float Nearest arg1) (of_float Nearest arg2))) result)) let test_FI_sub = QCheck.Test.make ~name:"test_FI_sub" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.sub (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (to_float (sub Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && - (FI.leq (FI.of_const (to_float (sub Down (of_float Nearest arg1) (of_float Nearest arg2))))) result) + (FI.leq (FI.of_const (sub Up (of_float Nearest arg1) (of_float Nearest arg2))) result) && + (FI.leq (FI.of_const (sub Down (of_float Nearest arg1) (of_float Nearest arg2)))) result) let test_FI_mul = QCheck.Test.make ~name:"test_FI_mul" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.mul (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (to_float (mul Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && - (FI.leq (FI.of_const (to_float (mul Down (of_float Nearest arg1) (of_float Nearest arg2)))) result)) + (FI.leq (FI.of_const (mul Up (of_float Nearest arg1) (of_float Nearest arg2))) result) && + (FI.leq (FI.of_const (mul Down (of_float Nearest arg1) (of_float Nearest arg2))) result)) let test_FI_div = QCheck.Test.make ~name:"test_FI_div" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.div (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (to_float (div Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && - (FI.leq (FI.of_const (to_float (div Down (of_float Nearest arg1) (of_float Nearest arg2)))) result)) + (FI.leq (FI.of_const (div Up (of_float Nearest arg1) (of_float Nearest arg2))) result) && + (FI.leq (FI.of_const (div Down (of_float Nearest arg1) (of_float Nearest arg2))) result)) let test () = [ From f990f47f0baec95a744e3784ceadcce7f6b0e2cd Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 6 Jun 2024 12:40:44 +0300 Subject: [PATCH 188/566] Replace usages of `Option.get` with `Option.fold` --- src/cdomain/value/cdomains/intDomain.ml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 3bf81b1ba1..f0ba026ccb 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -2256,9 +2256,8 @@ struct shift_op a b in (* If one of the parameters of the shift is negative, the result is undefined *) - let x_min = minimal x in - let y_min = minimal y in - if x_min = None || y_min = None || Z.compare (Option.get x_min) Z.zero < 0 || Z.compare (Option.get y_min) Z.zero < 0 then + let is_negative = Stdlib.Option.fold ~none:true ~some:(fun x -> Z.compare x Z.zero < 0) in + if is_negative (minimal x) || is_negative (minimal y) then top_of ik else norm ik @@ lift2 shift_op_big_int ik x y @@ -2616,9 +2615,8 @@ module Enums : S with type int_t = Z.t = struct shift_op a b in (* If one of the parameters of the shift is negative, the result is undefined *) - let x_min = minimal x in - let y_min = minimal y in - if x_min = None || y_min = None || Z.compare (Option.get x_min) Z.zero < 0 || Z.compare (Option.get y_min) Z.zero < 0 then + let is_negative = Stdlib.Option.fold ~none:true ~some:(fun x -> Z.compare x Z.zero < 0) in + if is_negative (minimal x) || is_negative (minimal y) then top_of ik else lift2 shift_op_big_int ik x y) From d61ec3e2ee08e8595828abad3964fb95f176b855 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 6 Jun 2024 12:45:19 +0300 Subject: [PATCH 189/566] Remove `open Batteries` --- src/cdomain/value/cdomains/intDomain.ml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index f0ba026ccb..69125a0d7a 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -2256,7 +2256,7 @@ struct shift_op a b in (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = Stdlib.Option.fold ~none:true ~some:(fun x -> Z.compare x Z.zero < 0) in + let is_negative = Option.fold ~none:true ~some:(fun x -> Z.compare x Z.zero < 0) in if is_negative (minimal x) || is_negative (minimal y) then top_of ik else @@ -2403,7 +2403,6 @@ module Booleans = MakeBooleans ( (* Inclusion/Exclusion sets. Go to top on arithmetic operations (except for some easy cases, e.g. multiplication with 0). Joins on widen, i.e. precise integers as long as not derived from arithmetic expressions. *) module Enums : S with type int_t = Z.t = struct - open Batteries module R = Interval32 (* range for exclusion *) let range_ikind = Cil.IInt @@ -2513,7 +2512,8 @@ module Enums : S with type int_t = Z.t = struct let ex = if Z.gt x Z.zero || Z.lt y Z.zero then BISet.singleton Z.zero else BISet.empty () in norm ik @@ (Exc (ex, r)) - let join ik = curry @@ function + let join _ x y = + match x, y with | Inc x, Inc y -> Inc (BISet.union x y) | Exc (x,r1), Exc (y,r2) -> Exc (BISet.inter x y, R.join r1 r2) | Exc (x,r), Inc y @@ -2521,13 +2521,14 @@ module Enums : S with type int_t = Z.t = struct let r = if BISet.is_empty y then r else - let (min_el_range, max_el_range) = Tuple2.mapn (fun x -> R.of_interval range_ikind (Size.min_range_sign_agnostic x)) (BISet.min_elt y, BISet.max_elt y) in + let (min_el_range, max_el_range) = Batteries.Tuple2.mapn (fun x -> R.of_interval range_ikind (Size.min_range_sign_agnostic x)) (BISet.min_elt y, BISet.max_elt y) in let range = R.join min_el_range max_el_range in R.join r range in Exc (BISet.diff x y, r) - let meet ikind = curry @@ function + let meet _ x y = + match x, y with | Inc x, Inc y -> Inc (BISet.inter x y) | Exc (x,r1), Exc (y,r2) -> let r = R.meet r1 r2 in @@ -2581,7 +2582,8 @@ module Enums : S with type int_t = Z.t = struct try lift2 f ikind a b with Division_by_zero -> top_of ikind let neg ?no_ov = lift1 Z.neg - let add ?no_ov ikind = curry @@ function + let add ?no_ov ikind a b = + match a, b with | Inc z,x when BISet.is_singleton z && BISet.choose z = Z.zero -> x | x,Inc z when BISet.is_singleton z && BISet.choose z = Z.zero -> x | x,y -> lift2 Z.add ikind x y @@ -2615,7 +2617,7 @@ module Enums : S with type int_t = Z.t = struct shift_op a b in (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = Stdlib.Option.fold ~none:true ~some:(fun x -> Z.compare x Z.zero < 0) in + let is_negative = Option.fold ~none:true ~some:(fun x -> Z.compare x Z.zero < 0) in if is_negative (minimal x) || is_negative (minimal y) then top_of ik else From a3d43f673debd47eb5d95c7b2d1cdf1bdd9cf8a3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Jun 2024 13:31:43 +0300 Subject: [PATCH 190/566] Add SKIP to Apron tests (PR #1500) --- tests/regression/46-apron2/87-unlock-idx-ambiguous.c | 2 +- tests/regression/46-apron2/88-unlock-unknown.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/46-apron2/87-unlock-idx-ambiguous.c b/tests/regression/46-apron2/87-unlock-idx-ambiguous.c index 97cada5369..ff6461217e 100644 --- a/tests/regression/46-apron2/87-unlock-idx-ambiguous.c +++ b/tests/regression/46-apron2/87-unlock-idx-ambiguous.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions --set ana.path_sens[+] threadflag // TODO: why nonterm without threadflag path-sens? #include #include diff --git a/tests/regression/46-apron2/88-unlock-unknown.c b/tests/regression/46-apron2/88-unlock-unknown.c index 3e09ee8659..dbdc4b9f77 100644 --- a/tests/regression/46-apron2/88-unlock-unknown.c +++ b/tests/regression/46-apron2/88-unlock-unknown.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --enable ana.sv-comp.functions #include #include From 8998e4776338305956ac6681fb751ba2a0e6bcba Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 6 Jun 2024 17:37:39 +0300 Subject: [PATCH 191/566] Add enabled_if to Apron cram tests (PR #1500) --- tests/regression/46-apron2/dune | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/regression/46-apron2/dune b/tests/regression/46-apron2/dune index f88a15d505..89efde3083 100644 --- a/tests/regression/46-apron2/dune +++ b/tests/regression/46-apron2/dune @@ -10,4 +10,6 @@ (action (chdir ../../.. (run %{update_suite} group apron2 -q)))) (cram + (alias runaprontest) + (enabled_if %{lib-available:apron}) (deps (glob_files *.c))) From b9badb75d61cc013352afdb39e3bd29aad3ff53f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Sat, 8 Jun 2024 11:19:22 +0300 Subject: [PATCH 192/566] Bump and pin apron to v0.9.15 For breaking change: https://github.com/antoinemine/apron/pull/108. --- dune-project | 2 +- goblint.opam | 6 +++++- goblint.opam.locked | 6 +++++- goblint.opam.template | 1 + 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/dune-project b/dune-project index 878abd3b4f..751876f6b0 100644 --- a/dune-project +++ b/dune-project @@ -54,7 +54,7 @@ conf-gcc ; ensures opam-repository CI installs real gcc from homebrew on MacOS ) (depopts - apron + (apron (>= v0.9.15)) z3 ) (conflicts diff --git a/goblint.opam b/goblint.opam index 692625c965..ec6a1f8fcb 100644 --- a/goblint.opam +++ b/goblint.opam @@ -51,7 +51,10 @@ depends: [ "benchmark" {with-test} "conf-gcc" ] -depopts: ["apron" "z3"] +depopts: [ + "apron" {>= "v0.9.15"} + "z3" +] conflicts: [ "result" {< "1.5"} "ez-conf-lib" {= "1"} @@ -81,6 +84,7 @@ pin-depends: [ [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] + [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#b3172bd51e4bb0135716a300cab424a56970d96f" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.locked b/goblint.opam.locked index f8de683948..8b184e189d 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -22,7 +22,7 @@ doc: "https://goblint.readthedocs.io/en/latest/" bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ "angstrom" {= "0.15.0"} - "apron" {= "v0.9.14~beta.2"} + "apron" {= "v0.9.15"} "arg-complete" {= "0.1.0"} "astring" {= "0.8.5"} "base-bigarray" {= "base"} @@ -140,5 +140,9 @@ pin-depends: [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] + [ + "apron.v0.9.15" + "git+https://github.com/antoinemine/apron.git#b3172bd51e4bb0135716a300cab424a56970d96f" + ] ] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.template b/goblint.opam.template index 2d5ef10bc9..27be21015f 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -6,6 +6,7 @@ pin-depends: [ [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] + [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#b3172bd51e4bb0135716a300cab424a56970d96f" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} From ec675d4afc599d86b00e3b25b4f04946215f225f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Sat, 8 Jun 2024 11:26:27 +0300 Subject: [PATCH 193/566] Use Apron.Environment.cmp --- src/cdomains/apron/affineEqualityDomain.apron.ml | 4 ++-- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 7e60cce74b..75c12a0e63 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -257,7 +257,7 @@ struct let meet t1 t2 = timing_wrap "meet" (meet t1) t2 let leq t1 t2 = - let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) + let env_comp = Environment.cmp t1.env t2.env in (* Apron's Environment.cmp has defined return values. *) if env_comp = -2 || env_comp > 0 then (* -2: environments are not compatible (a variable has different types in the 2 environements *) (* -1: if env1 is a subset of env2, (OK) *) @@ -334,7 +334,7 @@ struct else match Option.get a.d, Option.get b.d with | x, y when is_top_env a || is_top_env b -> {d = Some (Matrix.empty ()); env = Environment.lce a.env b.env} - | x, y when (Environment.compare a.env b.env <> 0) -> + | x, y when (Environment.cmp a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in let mod_y = dim_add (Environment.dimchange b.env sup_env) y in diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 67bd67f4e5..b5f3bb5304 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -386,7 +386,7 @@ struct let meet t1 t2 = timing_wrap "meet" (meet t1) t2 let leq t1 t2 = - let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) + let env_comp = Environment.cmp t1.env t2.env in (* Apron's Environment.cmp has defined return values. *) let implies ts i (var, b) = let tuple_cmp = Tuple2.eq (Option.eq ~eq:Int.equal) (Z.equal) in match var with @@ -450,7 +450,7 @@ struct | Some x, Some y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env in top_of new_env - | Some x, Some y when (Environment.compare a.env b.env <> 0) -> + | Some x, Some y when (Environment.cmp a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in let mod_y = dim_add (Environment.dimchange b.env sup_env) y in From 794b6ec21cc24bbd790518ede62bb85bbce5b98e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Sat, 8 Jun 2024 11:28:50 +0300 Subject: [PATCH 194/566] Implement GobApron.Environment.compare with TODO --- src/cdomains/apron/gobApron.apron.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml index c39a3e42db..40867f4889 100644 --- a/src/cdomains/apron/gobApron.apron.ml +++ b/src/cdomains/apron/gobApron.apron.ml @@ -42,6 +42,10 @@ module Environment = struct include Environment + let compare (x: t) (y: t): int = + (* TODO: implement total Environment order in OCaml *) + failwith "Apron.Environment doesn't have total order" (* https://github.com/antoinemine/apron/issues/99 *) + let ivars_only env = let ivs, fvs = Environment.vars env in assert (Array.length fvs = 0); (* shouldn't ever contain floats *) From a371b96d6c08177ff90545fc490ca40c69917077 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Sat, 8 Jun 2024 11:35:02 +0300 Subject: [PATCH 195/566] Remove broken Apron.Abstract1.compare --- src/cdomains/apron/apronDomain.apron.ml | 6 +++--- src/cdomains/apron/gobApron.apron.ml | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index d0ef268ca6..9bf17b2f92 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -451,9 +451,9 @@ struct let hash (x:t) = A.hash Man.mgr x - let compare (x:t) y: int = - (* there is no A.compare, but polymorphic compare should delegate to Abstract0 and Environment compare's implemented in Apron's C *) - Stdlib.compare x y + let compare (x: t) (y: t): int = + failwith "Apron.Abstract1 doesn't have total order" (* https://github.com/antoinemine/apron/issues/99 *) + let printXml f x = BatPrintf.fprintf f "\n\n\nconstraints\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%a" A.print x)) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x))) let to_yojson (x: t) = diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml index 40867f4889..46a0bbc28d 100644 --- a/src/cdomains/apron/gobApron.apron.ml +++ b/src/cdomains/apron/gobApron.apron.ml @@ -12,7 +12,9 @@ struct include Lincons1 let show = Format.asprintf "%a" print - let compare x y = String.compare (show x) (show y) (* HACK *) + let compare x y = + (* TODO: implement proper total Lincons1 order *) + String.compare (show x) (show y) (* HACK *) let num_vars x = (* Apron.Linexpr0.get_size returns some internal nonsense, so we count ourselves. *) From 36328e613fce1d102c99aae01b0ae4022c822e72 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Mon, 10 Jun 2024 12:49:02 +0300 Subject: [PATCH 196/566] Revert "Define CFloatType t = float" This reverts commit 1cbf7aca72bfbc8073a223f13723080b0d3c67ac. --- src/common/cdomains/floatOps/floatOps.ml | 4 ++-- src/common/cdomains/floatOps/floatOps.mli | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/cdomains/floatOps/floatOps.ml b/src/common/cdomains/floatOps/floatOps.ml index 6088fa9447..214b0d6c04 100644 --- a/src/common/cdomains/floatOps/floatOps.ml +++ b/src/common/cdomains/floatOps/floatOps.ml @@ -6,7 +6,7 @@ type round_mode = | Down module type CFloatType = sig - type t = float + type t val name: string val zero: t @@ -95,7 +95,7 @@ module CDouble = struct external atof: round_mode -> string -> t = "atof_double" end -module CFloat : CFloatType = struct +module CFloat = struct type t = float [@@deriving eq, ord, hash, to_yojson] let name = "float" diff --git a/src/common/cdomains/floatOps/floatOps.mli b/src/common/cdomains/floatOps/floatOps.mli index 2deb55a764..f956fe426d 100644 --- a/src/common/cdomains/floatOps/floatOps.mli +++ b/src/common/cdomains/floatOps/floatOps.mli @@ -7,7 +7,7 @@ type round_mode = | Down module type CFloatType = sig - type t = float + type t val name: string val zero: t From 682ce29f7a356abb269ded03e9bfcf0030f6ca3b Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Mon, 10 Jun 2024 12:49:10 +0300 Subject: [PATCH 197/566] Revert "Remove identity function `to_float`" This reverts commit 87f60f3b16b25261b29f3e999cc7d53b90ade7da. --- src/cdomain/value/cdomains/floatDomain.ml | 4 ++-- src/common/cdomains/floatOps/floatOps.ml | 3 +++ src/common/cdomains/floatOps/floatOps.mli | 1 + tests/unit/cdomains/floatDomainTest.ml | 29 ++++++++++++----------- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/cdomain/value/cdomains/floatDomain.ml b/src/cdomain/value/cdomains/floatDomain.ml index db6e4eccb5..0977c68890 100644 --- a/src/cdomain/value/cdomains/floatDomain.ml +++ b/src/cdomain/value/cdomains/floatDomain.ml @@ -241,12 +241,12 @@ module FloatIntervalImpl(Float_t : CFloatType) = struct let minimal = function | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "minimal %s" (show Bot))) - | Interval (l, _) -> Some l + | Interval (l, _) -> Some (Float_t.to_float l) | _ -> None let maximal = function | Bot -> raise (ArithmeticOnFloatBot (Printf.sprintf "maximal %s" (show Bot))) - | Interval (_, h) -> Some h + | Interval (_, h) -> Some (Float_t.to_float h) | _ -> None let is_exact = function diff --git a/src/common/cdomains/floatOps/floatOps.ml b/src/common/cdomains/floatOps/floatOps.ml index 214b0d6c04..eced6af248 100644 --- a/src/common/cdomains/floatOps/floatOps.ml +++ b/src/common/cdomains/floatOps/floatOps.ml @@ -16,6 +16,7 @@ module type CFloatType = sig val pi : t val of_float: round_mode -> float -> t + val to_float: t -> float val to_big_int: t -> Z.t val is_finite: t -> bool @@ -67,6 +68,7 @@ module CDouble = struct let pi = Float.pi let of_float _ x = x + let to_float x = x let to_big_int = big_int_of_float let is_finite = Float.is_finite @@ -110,6 +112,7 @@ module CFloat = struct let smallest = smallest' () let pi = pi' () + let to_float x = x let to_big_int = big_int_of_float let is_finite x = Float.is_finite x && x >= lower_bound && x <= upper_bound diff --git a/src/common/cdomains/floatOps/floatOps.mli b/src/common/cdomains/floatOps/floatOps.mli index f956fe426d..a07582f442 100644 --- a/src/common/cdomains/floatOps/floatOps.mli +++ b/src/common/cdomains/floatOps/floatOps.mli @@ -17,6 +17,7 @@ module type CFloatType = sig val pi : t val of_float: round_mode -> float -> t + val to_float: t -> float val to_big_int: t -> Z.t val is_finite: t -> bool diff --git a/tests/unit/cdomains/floatDomainTest.ml b/tests/unit/cdomains/floatDomainTest.ml index 6b49d21a1a..c6cb18500a 100644 --- a/tests/unit/cdomains/floatDomainTest.ml +++ b/tests/unit/cdomains/floatDomainTest.ml @@ -7,18 +7,19 @@ struct module FI = Domain_t module IT = IntDomain.IntDomTuple + let to_float = Float_t.to_float let of_float = Float_t.of_float let add = Float_t.add let sub = Float_t.sub let mul = Float_t.mul let div = Float_t.div - let pred x = Float_t.pred (of_float Nearest x) - let succ x = Float_t.succ (of_float Nearest x) + let pred x = to_float (Float_t.pred (of_float Nearest x)) + let succ x = to_float (Float_t.succ (of_float Nearest x)) - let fmax = Float_t.upper_bound - let fmin = Float_t.lower_bound - let fsmall = Float_t.smallest + let fmax = to_float Float_t.upper_bound + let fmin = to_float Float_t.lower_bound + let fsmall = to_float Float_t.smallest let fi_zero = FI.of_const 0. let fi_one = FI.of_const 1. @@ -52,7 +53,7 @@ struct FI.top () + FI.top () = FI.top (); (FI.of_const fmin) + (FI.of_const fmax) = fi_zero; (FI.of_const fsmall) + (FI.of_const fsmall) = FI.of_const (fsmall +. fsmall); - let one_plus_fsmall = Float_t.add Up (Float_t.of_float Up 1.) Float_t.smallest in + let one_plus_fsmall = to_float (Float_t.add Up (Float_t.of_float Up 1.) Float_t.smallest) in (FI.of_const fsmall) + (FI.of_const 1.) = FI.of_interval (1., one_plus_fsmall); (FI.of_interval (1., 2.)) + (FI.of_interval (2., 3.)) = FI.of_interval (3., 5.); (FI.of_interval (-. 2., 3.)) + (FI.of_interval (-. 100., 20.)) = FI.of_interval (-. 102., 23.); @@ -285,27 +286,27 @@ struct let test_FI_add = QCheck.Test.make ~name:"test_FI_add" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.add (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (add Up (of_float Nearest arg1) (of_float Nearest arg2))) result) && - (FI.leq (FI.of_const (add Down (of_float Nearest arg1) (of_float Nearest arg2))) result)) + (FI.leq (FI.of_const (to_float (add Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && + (FI.leq (FI.of_const (to_float (add Down (of_float Nearest arg1) (of_float Nearest arg2)))) result)) let test_FI_sub = QCheck.Test.make ~name:"test_FI_sub" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.sub (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (sub Up (of_float Nearest arg1) (of_float Nearest arg2))) result) && - (FI.leq (FI.of_const (sub Down (of_float Nearest arg1) (of_float Nearest arg2)))) result) + (FI.leq (FI.of_const (to_float (sub Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && + (FI.leq (FI.of_const (to_float (sub Down (of_float Nearest arg1) (of_float Nearest arg2))))) result) let test_FI_mul = QCheck.Test.make ~name:"test_FI_mul" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.mul (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (mul Up (of_float Nearest arg1) (of_float Nearest arg2))) result) && - (FI.leq (FI.of_const (mul Down (of_float Nearest arg1) (of_float Nearest arg2))) result)) + (FI.leq (FI.of_const (to_float (mul Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && + (FI.leq (FI.of_const (to_float (mul Down (of_float Nearest arg1) (of_float Nearest arg2)))) result)) let test_FI_div = QCheck.Test.make ~name:"test_FI_div" (QCheck.pair QCheck.float QCheck.float) (fun (arg1, arg2) -> let result = FI.div (FI.of_const arg1) (FI.of_const arg2) in - (FI.leq (FI.of_const (div Up (of_float Nearest arg1) (of_float Nearest arg2))) result) && - (FI.leq (FI.of_const (div Down (of_float Nearest arg1) (of_float Nearest arg2))) result)) + (FI.leq (FI.of_const (to_float (div Up (of_float Nearest arg1) (of_float Nearest arg2)))) result) && + (FI.leq (FI.of_const (to_float (div Down (of_float Nearest arg1) (of_float Nearest arg2)))) result)) let test () = [ From aed4cec86fb4c66c8b134ff9ea7a420327e49fb3 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Mon, 10 Jun 2024 14:00:18 +0200 Subject: [PATCH 198/566] added treatment of speculative computations in relational analyses --- src/cdomains/apron/sharedFunctions.apron.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 827dc252fc..e163ebf8ff 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -104,12 +104,13 @@ struct let texpr1_expr_of_cil_exp (ask:Queries.ask) d env exp no_ov = let conv exp = let query e ik = + if M.tracing then M.trace "relation-query" "before: texpr1_expr_of_cil_exp/query: %a" d_plainexp e; let res = match ask.f (EvalInt e) with | `Bot -> raise (Unsupported_CilExp Exp_not_supported) (* This should never happen according to Michael Schwarz *) | `Top -> IntDomain.IntDomTuple.top_of ik | `Lifted x -> x (* Cast should be unnecessary because it should be taken care of by EvalInt. *) in - if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp/query: %a -> %a" d_plainexp e IntDomain.IntDomTuple.pretty res; + if M.tracing then M.trace "relation-query" "texpr1_expr_of_cil_exp/query: %a -> %a" d_plainexp e IntDomain.IntDomTuple.pretty res; res in (* recurse without env and ask arguments *) @@ -138,10 +139,12 @@ struct this query is answered by the 2 var equalities domain itself. This normalizes arbitrary expressions to a point where they might be able to be represented by means of 2 var equalities *) let simplify e = + AnalysisState.executing_speculative_computations := true; let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in let simp = query e ikind in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp/simplify: %a -> %a" d_plainexp e IntDomain.IntDomTuple.pretty simp; + AnalysisState.executing_speculative_computations := false; BatOption.map_default (fun c -> Const (CInt (c, ikind, None))) e const in let texpr1 e = texpr1_expr_of_cil_exp (simplify e) in From 3df598090095223e6c4394bb271fa21e440861f8 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 11 Jun 2024 10:22:39 +0200 Subject: [PATCH 199/566] tweaks from MS' PR comments --- .../apron/linearTwoVarEqualityDomain.apron.ml | 61 +++++++++---------- src/cdomains/apron/sharedFunctions.apron.ml | 4 +- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 923229ca96..069983344e 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -36,17 +36,17 @@ module Rhs = struct let rhs=show_rhs_formatted (Printf.sprintf "var_%d") (v,o,d) in if not (Z.equal d Z.one) then "(" ^ rhs ^ ")/" ^ (Z.to_string d) else rhs - (** factor out gcd from all terms, i.e. ax=by+c is the canonical form for adx+bdy+cd *) + (** factor out gcd from all terms, i.e. ax=by+c with a positive is the canonical form for adx+bdy+cd *) let canonicalize (v,o,d) = let gcd = Z.gcd o d in (* gcd of coefficients *) let gcd = Option.map_default (fun (c,_) -> Z.gcd c gcd) gcd v in (* include monomial in gcd computation *) - let commondivisor = if Z.(lt d zero) then Z.neg gcd else gcd in (* cannonical form dictates d being positive *) - (BatOption.map (fun (coeff,i) -> (Z.div coeff commondivisor,i)) v,Z.div o commondivisor, Z.div d commondivisor) + let commondivisor = if Z.(lt d zero) then Z.neg gcd else gcd in (* canonical form dictates d being positive *) + (BatOption.map (fun (coeff,i) -> (Z.div coeff commondivisor,i)) v, Z.div o commondivisor, Z.div d commondivisor) (** Substitute rhs for varx in rhs' *) let subst rhs varx rhs' = match rhs,rhs' with - | (monom,o,d),(Some (c',x'),o',d') when x'=varx -> canonicalize (Option.map (fun (c,x) -> (Z.mul c c',x)) monom,Z.((o*c')+(d*o')),Z.mul d d') + | (monom, o, d), (Some (c', x'), o', d') when x'=varx -> canonicalize (Option.map (fun (c,x) -> (Z.mul c c',x)) monom, Z.((o*c')+(d*o')), Z.mul d d') | _ -> rhs' end @@ -59,7 +59,7 @@ module EqualitiesConjunction = struct let show_formatted formatter econ = if IntMap.is_empty econ then "{}" else - let str = IntMap.fold (fun i (ref,off,divi) acc -> Printf.sprintf "%s%s=%s ∧ %s" (Rhs.show_coeff divi) (formatter i) (Rhs.show_rhs_formatted formatter (ref,off,divi)) acc) econ "" in + let str = IntMap.fold (fun i (refmonom,off,divi) acc -> Printf.sprintf "%s%s=%s ∧ %s" (Rhs.show_coeff divi) (formatter i) (Rhs.show_rhs_formatted formatter (refmonom,off,divi)) acc) econ "" in "{" ^ String.sub str 0 (String.length str - 4) ^ "}" let show econ = show_formatted (Printf.sprintf "var_%d") econ @@ -76,7 +76,7 @@ module EqualitiesConjunction = struct let nontrivial (_,econmap) lhs = IntMap.mem lhs econmap (** turn x = (cy+o)/d into y = (dx-o)/c*) - let inverse x (c,y,o,d) = (y,(Some (d,x),Z.neg o,c)) + let inverse x (c,y,o,d) = (y, (Some (d, x), Z.neg o, c)) (** sparse implementation of get rhs for lhs, but will default to no mapping for sparse entries *) let get_rhs (_,econmap) lhs = IntMap.find_default (Rhs.var_zero lhs) lhs econmap @@ -89,10 +89,9 @@ module EqualitiesConjunction = struct IntMap.add lhs rhs map ) - (** canonicalize equation, and set_rhs, staying loyal to immutable, sparse map underneath,*) + (** canonicalize equation, and set_rhs, staying loyal to immutable, sparse map underneath *) let canonicalize_and_set (dim,map) lhs rhs = set_rhs (dim,map) lhs (Rhs.canonicalize rhs) - (** add a new equality to the domain *) let copy = identity @@ -104,7 +103,7 @@ module EqualitiesConjunction = struct let offsetlist = Array.to_list indexes in let rec bumpvar delta i = function (* bump the variable i by delta; find delta by counting indices in offsetlist until we reach a larger index then our current parameter *) | head::rest when i>=head -> bumpvar (delta+1) i rest (* rec call even when =, in order to correctly interpret double bumps *) - | _ (* i let res = op i delta in res + | _ (* i op i delta in let memobumpvar = (* Memoized version of bumpvar *) let module IntHash = struct type t = int [@@deriving eq,hash] end in @@ -117,7 +116,8 @@ module EqualitiesConjunction = struct IntHashtbl.add h x r; r) in - let rec bumpentry k (refvar,offset,divi) = function (* directly bumps lhs-variable during a run through indexes, bumping refvar explicitely with a new lookup in indexes *) + let rec bumpentry k (refvar,offset,divi) = function (* directly bumps lhs-variable during a run through indexes, bumping refvar explicitly with a new lookup in indexes *) + | (tbl,delta,head::rest) when k>=head -> bumpentry k (refvar,offset,divi) (tbl,delta+1,rest) (* rec call even when =, in order to correctly interpret double bumps *) | (tbl,delta,lyst) (* k (IntMap.add (op k delta) (BatOption.map (fun (c,v) -> (c,memobumpvar v)) refvar,offset,divi) tbl, delta, lyst) in @@ -148,7 +148,7 @@ module EqualitiesConjunction = struct if M.tracing then M.trace "forget" "headvar var_%d" var; (* var is the reference variable of its connected component *) (let cluster = List.sort (Int.compare) @@ IntMap.fold - (fun i (ref,_,_) l -> BatOption.map_default (fun (coeff,ref) -> if (ref=ref_var) then i::l else l) l ref) (snd d) [] in + (fun i (refe,_,_) l -> BatOption.map_default (fun (coeff,refe) -> if (refe=ref_var) then i::l else l) l refe) (snd d) [] in if M.tracing then M.trace "forget" "cluster varindices: [%s]" (String.concat ", " (List.map (string_of_int) cluster)); (* obtain cluster with common reference variable ref_var*) match cluster with (* new ref_var is taken from head of the cluster *) @@ -157,7 +157,7 @@ module EqualitiesConjunction = struct (* divi*x = coeff*y + offs =inverse=> y =( divi*x - offs)/coeff *) let (newref,offs,divi) = (get_rhs d head) in let (coeff,y) = BatOption.get newref in - let (y,yrhs)= inverse head (coeff,y,offs,divi) in (* reassemble yrhs out of components *) + let (y,yrhs) = inverse head (coeff,y,offs,divi) in (* reassemble yrhs out of components *) let shifted_cluster = (List.fold (fun map i -> let irhs = (get_rhs d i) in (* old entry is i = irhs *) Rhs.subst yrhs y irhs |> (* new entry for i is irhs [yrhs/y] *) @@ -198,14 +198,15 @@ module EqualitiesConjunction = struct let meet_with_one_conj ts i (var, offs, divi) = let (var,offs,divi) = Rhs.canonicalize (var,offs,divi) in (* make sure that the one new conj is properly canonicalized *) let res = - let subst_var tsi x (vary, o, d) = + let subst_var (dim,econj) x (vary, o, d) = (* [[x substby (cy+o)/d ]] ((c'x+o')/d') *) (* =====> (c'cy + c'o+o'd)/(dd') *) let adjust = function - | (Some (c',varx), o',d') when varx = x -> Rhs.canonicalize (BatOption.map (fun (c,y)->(Z.mul c c',y)) vary, Z.((c'*o)+(o'*d)),Z.(d'*d)) + | (Some (c',varx), o',d') when varx = x -> + let open Z in Rhs.canonicalize (BatOption.map (fun (c, y)-> (c * c', y)) vary, c'*o + o'*d, d'*d) | e -> e in - (fst tsi, IntMap.add x (vary, o, d) @@ IntMap.map adjust (snd tsi)) (* in case of sparse representation, make sure that the equality is now included in the conjunction *) + (dim, IntMap.add x (vary, o, d) @@ IntMap.map adjust econj) (* in case of sparse representation, make sure that the equality is now included in the conjunction *) in (match var, (get_rhs ts i) with (*| new conj , old conj *) @@ -230,8 +231,8 @@ module EqualitiesConjunction = struct | (Some (coeff2,h2), o2, divi2) as normalizedj -> if h1 = h2 then (* this is the case where x_i and x_j already where in the same equivalence class; let's see whether the new equality contradicts the old one *) let normalizedi= Rhs.subst normalizedj j (Some(coeff,j),offs,divi) in - (if not @@ Rhs.equal normalizedi oldi then raise Contradiction else ts) - else if h1 < h2 (* good, we no unite the two equvalence classes; let's decide upon the representant *) + if not @@ Rhs.equal normalizedi oldi then raise Contradiction else ts + else if h1 < h2 (* good, we now unite the two equvalence classes; let's decide upon the representative *) then (* express h2 in terms of h1: *) let (_,newh2)= inverse j (coeff2,h2,o2,divi2) in let newh2 = Rhs.subst oldi i (Rhs.subst (snd @@ inverse i (coeff,j,offs,divi)) j newh2) in @@ -244,13 +245,10 @@ module EqualitiesConjunction = struct ; res (** affine transform variable i allover conj with transformer (Some (coeff,i)+offs)/divi *) - let affine_transform econ i (var,offs,divi) = - if nontrivial econ i then (** i cannot occur on any other rhs apart from itself *) - set_rhs econ i (Rhs.subst (get_rhs econ i) i (var,offs,divi)) + let affine_transform econ i (coeff, j, offs, divi) = + if nontrivial econ i then (* i cannot occur on any other rhs apart from itself *) + set_rhs econ i (Rhs.subst (get_rhs econ i) i (Some (coeff,j), offs, divi)) else (* var_i = var_i, i.e. it may occur on the rhs of other equalities *) - match var with - | None -> failwith "this is not a valid affine transformation" - | Some (coeff,j) -> (* so now, we transform with the inverse of the transformer: *) let inv = snd (inverse i (coeff,j,offs,divi)) in IntMap.fold (fun k v acc -> @@ -276,12 +274,10 @@ struct let open Apron.Texpr1 in let exception NotLinearExpr in let exception ScalarIsInfinity in - let negate coeff_var_list = List.map (function - | (Some(coeff,i),offs,divi) -> (Some(Z.neg coeff,i),Z.neg offs,divi) - | (None ,offs,divi) -> (None ,Z.neg offs,divi)) coeff_var_list in - let multiply_with_Q dividend divisor coeff_var_list = List.map (function - | (Some (coeff, var),offs,divi) -> Rhs.canonicalize (Some(Z.mul dividend coeff,var),Z.(dividend * offs),Z.mul divi divisor) - | (None,offs,divi) -> Rhs.canonicalize (None,Z.mul dividend offs,Z.mul divi divisor)) coeff_var_list in + let negate coeff_var_list = + List.map (fun (monom, offs, divi) -> Z.(BatOption.map (fun (coeff,i) -> (neg coeff, i)) monom, neg offs, divi)) coeff_var_list in + let multiply_with_Q dividend divisor coeff_var_list = + List.map (fun (monom, offs, divi) -> Rhs.canonicalize Z.(BatOption.map (fun (coeff,i) -> (dividend*coeff,i)) monom, dividend*offs, divi*divisor) ) coeff_var_list in let multiply a b = (* if one of them is a constant, then multiply. Otherwise, the expression is not linear *) match a, b with @@ -614,7 +610,7 @@ struct assign_const (forget_var t var) var_i off divi | Some (Some (coeff_var,exp_var), off, divi) when var_i = exp_var -> (* Statement "assigned_var = (coeff_var*assigned_var + off) / divi" *) - {d=Some (EConj.affine_transform d var_i (Some (coeff_var, var_i), off, divi)); env=t.env } + {d=Some (EConj.affine_transform d var_i (coeff_var, var_i, off, divi)); env=t.env } | Some (Some monomial, off, divi) -> (* Statement "assigned_var = (monomial) + off / divi" (assigned_var is not the same as exp_var) *) meet_with_one_conj (forget_var t var) var_i (Some (monomial), off, divi) @@ -742,10 +738,11 @@ struct | EQ -> (* c1*var1/d1 + c2*var2/d2 +constant/divisor = 0*) (* ======> c1*divisor*d2 * var1 = -c2*divisor*d1 * var2 +constant*-d1*d2*) (* \/ c2*divisor*d1 * var2 = -c1*divisor*d2 * var1 +constant*-d1*d2*) + let open Z in if var1 < var2 then - meet_with_one_conj t var2 (Rhs.canonicalize (Some (Z.neg @@ Z.(c1*divisor),var1),Z.neg @@ Z.(constant*d2*d1),Z.(c2*divisor*d1))) + meet_with_one_conj t var2 (Rhs.canonicalize (Some (neg @@ c1*divisor,var1),neg @@ constant*d2*d1,c2*divisor*d1)) else - meet_with_one_conj t var1 (Rhs.canonicalize (Some (Z.neg @@ Z.(c2*divisor),var2),Z.neg @@ Z.(constant*d2*d1),Z.(c1*divisor*d2))) + meet_with_one_conj t var1 (Rhs.canonicalize (Some (neg @@ c2*divisor,var2),neg @@ constant*d2*d1,c1*divisor*d2)) | _-> t (* Not supported in equality based 2vars without coeffiients *) end | _ -> t (* For equalities of more then 2 vars we just return t *)) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index e163ebf8ff..6b1993d381 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -104,7 +104,6 @@ struct let texpr1_expr_of_cil_exp (ask:Queries.ask) d env exp no_ov = let conv exp = let query e ik = - if M.tracing then M.trace "relation-query" "before: texpr1_expr_of_cil_exp/query: %a" d_plainexp e; let res = match ask.f (EvalInt e) with | `Bot -> raise (Unsupported_CilExp Exp_not_supported) (* This should never happen according to Michael Schwarz *) @@ -139,12 +138,11 @@ struct this query is answered by the 2 var equalities domain itself. This normalizes arbitrary expressions to a point where they might be able to be represented by means of 2 var equalities *) let simplify e = - AnalysisState.executing_speculative_computations := true; + GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in let simp = query e ikind in let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to ikind simp in if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp/simplify: %a -> %a" d_plainexp e IntDomain.IntDomTuple.pretty simp; - AnalysisState.executing_speculative_computations := false; BatOption.map_default (fun c -> Const (CInt (c, ikind, None))) e const in let texpr1 e = texpr1_expr_of_cil_exp (simplify e) in From ca31b6e0eb0b7be74c359fd127e1267274836fef Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 11 Jun 2024 10:27:29 +0200 Subject: [PATCH 200/566] added explanation --- src/cdomains/apron/sharedFunctions.apron.ml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 6b1993d381..8c94c996b0 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -136,7 +136,13 @@ struct let expr = (** simplify asks for a constant value of some subexpression e, similar to a constant fold. In particular but not exclusively this query is answered by the 2 var equalities domain itself. This normalizes arbitrary expressions to a point where they - might be able to be represented by means of 2 var equalities *) + might be able to be represented by means of 2 var equalities + + This simplification happens during a time, when there are temporary variables a#in and a#out part of the expression, + but are not represented in the ctx, thus queries may result in top for these variables. Wrapping this in speculative + mode is a stop-gap measure to avoid flagging overflows. We however should address simplification in a more generally useful way. + outside of the apron-related expression conversion. + *) let simplify e = GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> let ikind = try (Cilfacade.get_ikind_exp e) with Invalid_argument _ -> raise (Unsupported_CilExp Exp_not_supported) in From 87e1e33843caaddaee111560287585b2bb1e28f8 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 11 Jun 2024 12:26:38 +0300 Subject: [PATCH 201/566] Use GobOption.forall instead Option.fold --- src/cdomain/value/cdomains/intDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 69125a0d7a..7b3a8f955e 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -2256,7 +2256,7 @@ struct shift_op a b in (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = Option.fold ~none:true ~some:(fun x -> Z.compare x Z.zero < 0) in + let is_negative = GobOption.for_all (fun x -> Z.compare x Z.zero < 0) in if is_negative (minimal x) || is_negative (minimal y) then top_of ik else @@ -2617,7 +2617,7 @@ module Enums : S with type int_t = Z.t = struct shift_op a b in (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = Option.fold ~none:true ~some:(fun x -> Z.compare x Z.zero < 0) in + let is_negative = GobOption.for_all (fun x -> Z.compare x Z.zero < 0) in if is_negative (minimal x) || is_negative (minimal y) then top_of ik else From ae12610b61efeae7c91d9ccdfb50a0d342dc43d1 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 11 Jun 2024 12:27:10 +0300 Subject: [PATCH 202/566] Use Z.lt instead Z.compare --- src/cdomain/value/cdomains/intDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 7b3a8f955e..d75c12527b 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -2256,7 +2256,7 @@ struct shift_op a b in (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = GobOption.for_all (fun x -> Z.compare x Z.zero < 0) in + let is_negative = GobOption.for_all (fun x -> Z.lt x Z.zero) in if is_negative (minimal x) || is_negative (minimal y) then top_of ik else @@ -2617,7 +2617,7 @@ module Enums : S with type int_t = Z.t = struct shift_op a b in (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = GobOption.for_all (fun x -> Z.compare x Z.zero < 0) in + let is_negative = GobOption.for_all (fun x -> Z.lt x Z.zero) in if is_negative (minimal x) || is_negative (minimal y) then top_of ik else From f0c2c45c4003d222d8f86e6ee703f79f8a15b256 Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Tue, 11 Jun 2024 13:15:36 +0200 Subject: [PATCH 203/566] simplify threshold search for narrowing --- src/cdomain/value/cdomains/intDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index a67740e9db..efbab22b43 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -575,11 +575,11 @@ module IntervalArith (Ints_t : IntOps.IntOps) = struct let is_upper_threshold u = let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in let u = Ints_t.to_bigint u in - List.exists (fun x -> Z.compare u x = 0) ts + List.exists (Z.equal u) ts let is_lower_threshold l = let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in let l = Ints_t.to_bigint l in - List.exists (fun x -> Z.compare l x = 0) ts + List.exists (Z.equal l) ts end module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = From bde040e3f0214fdd966a5df9b8ef28dfa315581a Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Tue, 11 Jun 2024 15:42:47 +0200 Subject: [PATCH 204/566] frac_of_scalar instead of int_of_scalar --- src/cdomains/apron/sharedFunctions.apron.ml | 22 ++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index b287bb9bae..7c69cefda4 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -6,6 +6,14 @@ open GobApron module M = Messages +let frac_of_scalar scalar = + if Scalar.is_infty scalar <> 0 then (* infinity means unbounded *) + None + else match scalar with + | Float f -> if Stdlib.Float.is_integer f then Some (Q.of_float f) else None + | Mpqf f -> Some (Z_mlgmpidl.q_of_mpqf f) + | _ -> failwith "frac_of_scalar: unsupported" + let int_of_scalar ?round (scalar: Scalar.t) = if Scalar.is_infty scalar <> 0 then (* infinity means unbounded *) None @@ -20,21 +28,21 @@ let int_of_scalar ?round (scalar: Scalar.t) = | None when Stdlib.Float.is_integer f -> Some f | None -> None in - Q.make (Z.of_float f) Z.one + Z.of_float f | Mpqf scalar -> (* octMPQ, boxMPQ, polkaMPQ *) let n = Mpqf.get_num scalar in let d = Mpqf.get_den scalar in - let+ (n,d) = + let+ z = if Mpzf.cmp_int d 1 = 0 then (* exact integer (denominator 1) *) - Some (n,Mpzf.of_int 1) + Some n else begin match round with - | Some `Floor -> Some (Mpzf.fdiv_q n d, Mpzf.of_int 1) (* floor division *) - | Some `Ceil -> Some (Mpzf.cdiv_q n d, Mpzf.of_int 1) (* ceiling division *) - | None -> Some (n,d) + | Some `Floor -> Some (Mpzf.fdiv_q n d) (* floor division *) + | Some `Ceil -> Some (Mpzf.cdiv_q n d) (* ceiling division *) + | None -> None end in - Q.make (Z_mlgmpidl.z_of_mpzf n) (Z_mlgmpidl.z_of_mpzf d) + Z_mlgmpidl.z_of_mpzf z | _ -> failwith ("int_of_scalar: unsupported: " ^ Scalar.to_string scalar) From 60ecfe7eb23fcdd927eed52da45c9da755dc7fa3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 12 Jun 2024 10:03:42 +0300 Subject: [PATCH 205/566] Fix LinearTwoVarEqualityDomain indentation (PR #1466) --- .../apron/linearTwoVarEqualityDomain.apron.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 069983344e..5558bc2c96 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -249,13 +249,13 @@ module EqualitiesConjunction = struct if nontrivial econ i then (* i cannot occur on any other rhs apart from itself *) set_rhs econ i (Rhs.subst (get_rhs econ i) i (Some (coeff,j), offs, divi)) else (* var_i = var_i, i.e. it may occur on the rhs of other equalities *) - (* so now, we transform with the inverse of the transformer: *) - let inv = snd (inverse i (coeff,j,offs,divi)) in - IntMap.fold (fun k v acc -> - match v with - | (Some (c,x),o,d) when x=i-> set_rhs acc k (Rhs.subst inv i v) - | _ -> acc - ) (snd econ) econ + (* so now, we transform with the inverse of the transformer: *) + let inv = snd (inverse i (coeff,j,offs,divi)) in + IntMap.fold (fun k v acc -> + match v with + | (Some (c,x),o,d) when x=i-> set_rhs acc k (Rhs.subst inv i v) + | _ -> acc + ) (snd econ) econ end From 19164253deacf3d6140fd4e917c7bc5b58be395d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 12 Jun 2024 11:46:33 +0200 Subject: [PATCH 206/566] Introduce sync reason "SyncJoin" Helps avoid "sync" calls for function start nodes when threadflag is context-sensitive. Previsouly, it was only avoided when the flag was path-sensitive --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/apron/relationPriv.apron.ml | 80 +++++++++++++------- src/analyses/base.ml | 2 +- src/analyses/basePriv.ml | 43 +++++++++-- src/analyses/basePriv.mli | 2 +- src/analyses/commonPriv.ml | 15 ++++ src/framework/analyses.ml | 2 +- src/framework/constraints.ml | 5 +- tests/regression/46-apron2/89-sides-pp.c | 27 +++++++ 9 files changed, 136 insertions(+), 42 deletions(-) create mode 100644 tests/regression/46-apron2/89-sides-pp.c diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index 72b91d71b7..a42d78d71a 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -772,7 +772,7 @@ struct PCU.RH.replace results ctx.node new_value; end; WideningTokens.with_local_side_tokens (fun () -> - Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `Return | `Init | `Thread]) + Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread]) ) let init marshal = diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 61da6ddc42..6f75ddbfe5 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -36,7 +36,7 @@ module type S = val lock: Q.ask -> (V.t -> G.t) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t - val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `Return | `Init | `Thread] -> relation_components_t + val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> relation_components_t val escape: Node.t -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> EscapeDomain.EscapedVars.t -> relation_components_t val enter_multithreaded: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> relation_components_t @@ -96,8 +96,7 @@ struct { st with rel = rel_local } let sync (ask: Q.ask) getg sideg (st: relation_components_t) reason = - match reason with - | `Join when ConfCheck.branched_thread_creation () -> + let branched_sync () = if ask.f (Q.MustBeSingleThreaded {since_start = true}) then st else @@ -110,7 +109,14 @@ struct ) in {st with rel = rel_local} + in + match reason with + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation () -> + branched_sync () | `Join + | `JoinCall | `Normal | `Init | `Thread @@ -337,17 +343,8 @@ struct let thread_join ?(force=false) ask getg exp st = st let thread_return ask getg sideg tid st = st - let sync ask getg sideg (st: relation_components_t) reason = - match reason with - | `Return -> (* required for thread return *) - (* TODO: implement? *) - begin match ThreadId.get_current ask with - | `Lifted x (* when CPA.mem x st.cpa *) -> - st - | _ -> - st - end - | `Join when ConfCheck.branched_thread_creation () -> + let sync (ask:Q.ask) getg sideg (st: relation_components_t) reason = + let branched_sync () = if ask.f (Q.MustBeSingleThreaded { since_start= true }) then st else @@ -376,7 +373,22 @@ struct let rel_local' = RD.meet rel_local (getg ()) in {st with rel = rel_local'} *) st + in + match reason with + | `Return -> (* required for thread return *) + (* TODO: implement? *) + begin match ThreadId.get_current ask with + | `Lifted x (* when CPA.mem x st.cpa *) -> + st + | _ -> + st + end + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Normal | `Init | `Thread -> @@ -626,17 +638,8 @@ struct let thread_join ?(force=false) ask getg exp st = st let thread_return ask getg sideg tid st = st - let sync ask getg sideg (st: relation_components_t) reason = - match reason with - | `Return -> (* required for thread return *) - (* TODO: implement? *) - begin match ThreadId.get_current ask with - | `Lifted x (* when CPA.mem x st.cpa *) -> - st - | _ -> - st - end - | `Join when ConfCheck.branched_thread_creation () -> + let sync (ask:Q.ask) getg sideg (st: relation_components_t) reason = + let branched_sync () = if ask.f (Q.MustBeSingleThreaded {since_start = true}) then st else @@ -659,7 +662,22 @@ struct ) in {st with rel = rel_local} + in + match reason with + | `Return -> (* required for thread return *) + (* TODO: implement? *) + begin match ThreadId.get_current ask with + | `Lifted x (* when CPA.mem x st.cpa *) -> + st + | _ -> + st + end + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Normal | `Init | `Thread -> @@ -1192,9 +1210,7 @@ struct st let sync (ask:Q.ask) getg sideg (st: relation_components_t) reason = - match reason with - | `Return -> st (* TODO: implement? *) - | `Join when ConfCheck.branched_thread_creation () -> + let branched_sync () = if ask.f (Q.MustBeSingleThreaded {since_start = true}) then st else @@ -1209,7 +1225,15 @@ struct ) in {st with rel = rel_local} + in + match reason with + | `Return -> st (* TODO: implement? *) + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Normal | `Init | `Thread -> diff --git a/src/analyses/base.ml b/src/analyses/base.ml index b80ac59d7f..102e22b5a7 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -451,7 +451,7 @@ struct else ctx.local - let sync ctx reason = sync' (reason :> [`Normal | `Join | `Return | `Init | `Thread]) ctx + let sync ctx reason = sync' (reason :> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread]) ctx let publish_all ctx reason = ignore (sync' reason ctx) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index cecc838b9e..878a86d85e 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -31,7 +31,7 @@ sig val lock: Q.ask -> (V.t -> G.t) -> BaseComponents (D).t -> LockDomain.Addr.t -> BaseComponents (D).t val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> LockDomain.Addr.t -> BaseComponents (D).t - val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> [`Normal | `Join | `Return | `Init | `Thread] -> BaseComponents (D).t + val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> BaseComponents (D).t val escape: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> EscapeDomain.EscapedVars.t -> BaseComponents (D).t val enter_multithreaded: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> BaseComponents (D).t @@ -306,8 +306,8 @@ struct st let sync ask getg sideg (st: BaseComponents (D).t) reason = - match reason with - | `Join when ConfCheck.branched_thread_creation () -> (* required for branched thread creation *) + let branched_sync () = + (* required for branched thread creation *) let global_cpa = CPA.filter (fun x _ -> is_global ask x && is_unprotected ask x) st.cpa in sideg V.mutex_inits global_cpa; (* must be like enter_multithreaded *) (* TODO: this makes mutex-oplus less precise in 28-race_reach/10-ptrmunge_racefree and 28-race_reach/trylock2_racefree, why? *) @@ -318,7 +318,14 @@ struct sideg (V.global x) (CPA.singleton x v) ) st.cpa; st + in + match reason with + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Return | `Normal | `Init @@ -404,8 +411,8 @@ struct {st with cpa = cpa'} let sync ask getg sideg (st: BaseComponents (D).t) reason = - match reason with - | `Join when ConfCheck.branched_thread_creation () -> (* required for branched thread creation *) + let branched_sync () = + (* required for branched thread creation *) let global_cpa = CPA.filter (fun x _ -> is_global ask x && is_unprotected ask x) st.cpa in sideg V.mutex_inits global_cpa; (* must be like enter_multithreaded *) @@ -422,7 +429,14 @@ struct ) st.cpa st.cpa in {st with cpa = cpa'} + in + match reason with + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Return | `Normal | `Init @@ -772,9 +786,9 @@ struct ) st.cpa st let sync ask getg sideg (st: BaseComponents (D).t) reason = - let sideg = Wrapper.sideg ask sideg in - match reason with - | `Join when ConfCheck.branched_thread_creation () -> (* required for branched thread creation *) + let branched_sync () = + (* required for branched thread creation *) + let sideg = Wrapper.sideg ask sideg in CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_global ask x && is_unprotected ask x then ( sideg (V.unprotected x) v; @@ -784,7 +798,14 @@ struct else st ) st.cpa st + in + match reason with + | `Join when ConfCheck.branched_thread_creation () -> + branched_sync () + | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + branched_sync () | `Join + | `JoinCall | `Return | `Normal | `Init @@ -1034,6 +1055,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st @@ -1089,6 +1111,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st @@ -1160,6 +1183,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st @@ -1318,6 +1342,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st @@ -1496,6 +1521,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st @@ -1678,6 +1704,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) + | `JoinCall | `Init | `Thread -> st diff --git a/src/analyses/basePriv.mli b/src/analyses/basePriv.mli index e176a450fa..87b17e5ad0 100644 --- a/src/analyses/basePriv.mli +++ b/src/analyses/basePriv.mli @@ -20,7 +20,7 @@ sig val lock: Queries.ask -> (V.t -> G.t) -> BaseDomain.BaseComponents (D).t -> LockDomain.Addr.t -> BaseDomain.BaseComponents (D).t val unlock: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> LockDomain.Addr.t -> BaseDomain.BaseComponents (D).t - val sync: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> [`Normal | `Join | `Return | `Init | `Thread] -> BaseDomain.BaseComponents (D).t + val sync: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> BaseDomain.BaseComponents (D).t val escape: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> EscapeDomain.EscapedVars.t -> BaseDomain.BaseComponents (D).t val enter_multithreaded: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> BaseDomain.BaseComponents (D).t diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index fb5d0db142..9f87f35b38 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -59,6 +59,21 @@ struct not threadflag_path_sens else true + + (** Whether branched thread creation at start nodes of procedures needs to be handled by [sync `JoinCall] of privatization. *) + let branched_thread_creation_at_call () = + let threadflag_active = List.mem "threadflag" (GobConfig.get_string_list "ana.activated") in + if threadflag_active then + let sens = GobConfig.get_string_list "ana.ctx_sens" in + let threadflag_ctx_sens = match sens with + | [] -> (* use values of "ana.ctx_insens" (blacklist) *) + not (List.mem "threadflag" @@ GobConfig.get_string_list "ana.ctx_insens") + | sens -> (* use values of "ana.ctx_sens" (whitelist) *) + List.mem "threadflag" sens + in + not threadflag_ctx_sens + else + true end module Protection = diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 4ef98358f4..1ea01c99fb 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -209,7 +209,7 @@ sig val context: (D.t, G.t, C.t, V.t) ctx -> fundec -> D.t -> C.t val startcontext: unit -> C.t - val sync : (D.t, G.t, C.t, V.t) ctx -> [`Normal | `Join | `Return] -> D.t + val sync : (D.t, G.t, C.t, V.t) ctx -> [`Normal | `Join | `JoinCall | `Return] -> D.t val query : (D.t, G.t, C.t, V.t) ctx -> 'a Queries.t -> 'a Queries.result (** A transfer function which handles the assignment of a rval to a lval, i.e., diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 07180d54dd..6a1eae3ed1 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -624,9 +624,10 @@ struct let sync ctx = match ctx.prev_node, Cfg.prev ctx.prev_node with - | _, _ :: _ :: _ (* Join in CFG. *) - | FunctionEntry _, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) + | _, _ :: _ :: _ -> (* Join in CFG. *) S.sync ctx `Join + | FunctionEntry _, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) + S.sync ctx `JoinCall | _, _ -> S.sync ctx `Normal let side_context sideg f c = diff --git a/tests/regression/46-apron2/89-sides-pp.c b/tests/regression/46-apron2/89-sides-pp.c new file mode 100644 index 0000000000..10fc5f8d1e --- /dev/null +++ b/tests/regression/46-apron2/89-sides-pp.c @@ -0,0 +1,27 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet +// NOTIMEOUT +#include +#include + +int g; +pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + +void fn() { + // Just do nothing + return; +} + +void *t_fun(void *arg) { + pthread_mutex_lock(&m); + g = g+1; + fn(); + pthread_mutex_unlock(&m); + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + return 0; +} From 496b5ef26373e4e7b06832657f0ba196db671806 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 12 Jun 2024 18:05:34 +0200 Subject: [PATCH 207/566] Add misbehaving program with gas --- src/config/options.schema.json | 2 +- tests/regression/80-context_gas/21-sync.c | 35 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/regression/80-context_gas/21-sync.c diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 488fc494b0..acf85abed9 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -980,7 +980,7 @@ }, "gas_value": { "title": "ana.context.gas_value", - "description": "Denotes the gas value x for the ContextGasLifter. Negative values deactivate the context gas, zero yields a context-insensitve analysis. If enabled, the first x recursive calls of the call stack are analyzed context-sensitively. Any calls deeper in the call stack are analyzed with the same (empty) context.", + "description": "Denotes the gas value x for the ContextGasLifter. Negative values deactivate the context gas, zero yields a context-insensitive analysis. If enabled, the first x recursive calls of the call stack are analyzed context-sensitively. Any calls deeper in the call stack are analyzed with the same (empty) context.", "type": "integer", "default": -1 }, diff --git a/tests/regression/80-context_gas/21-sync.c b/tests/regression/80-context_gas/21-sync.c new file mode 100644 index 0000000000..5769ffaa67 --- /dev/null +++ b/tests/regression/80-context_gas/21-sync.c @@ -0,0 +1,35 @@ +// PARAM: --set ana.context.gas_value 3 +// Like 00/35 but with gas this time! +// Misbehaves for gas <= 3 +#include +#include + +int g = 1; + +void foo() { + // Single-threaded: g = 1 in local state + // Multi-threaded: g = 2 in global unprotected invariant + // Joined contexts: g is unprotected, so read g = 2 from global unprotected invariant (only) + // Was soundly claiming that check will succeed! + int x = g; + __goblint_check(x == 2); // UNKNOWN! +} + +void *t_fun(void *arg) { + foo(); +} + +int do_stuff() { + foo(); + g = 2; + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + return 0; +} + +int main() { + do_stuff(); + return 0; +} From e613e13d64ede078c23b85401a8176107f81d567 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 12 Jun 2024 18:53:17 +0200 Subject: [PATCH 208/566] Make sound for context gas --- src/analyses/apron/relationPriv.apron.ml | 6 ++-- src/analyses/basePriv.ml | 6 ++-- src/analyses/commonPriv.ml | 7 ++-- src/analyses/mCP.ml | 7 ++++ src/domains/queries.ml | 5 +++ src/framework/constraints.ml | 8 ++++- tests/regression/80-context_gas/21-sync.c | 2 +- .../80-context_gas/22-sync-precision.c | 36 +++++++++++++++++++ 8 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 tests/regression/80-context_gas/22-sync-precision.c diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 6f75ddbfe5..3ecfb713d7 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -385,7 +385,7 @@ struct end | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> branched_sync () | `Join | `JoinCall @@ -674,7 +674,7 @@ struct end | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> branched_sync () | `Join | `JoinCall @@ -1230,7 +1230,7 @@ struct | `Return -> st (* TODO: implement? *) | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> branched_sync () | `Join | `JoinCall diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 878a86d85e..efa0575bf4 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -322,7 +322,7 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> branched_sync () | `Join | `JoinCall @@ -433,7 +433,7 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> branched_sync () | `Join | `JoinCall @@ -802,7 +802,7 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call () -> + | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> branched_sync () | `Join | `JoinCall diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 9f87f35b38..1ece8dcc60 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -61,7 +61,7 @@ struct true (** Whether branched thread creation at start nodes of procedures needs to be handled by [sync `JoinCall] of privatization. *) - let branched_thread_creation_at_call () = + let branched_thread_creation_at_call (ask:Queries.ask) = let threadflag_active = List.mem "threadflag" (GobConfig.get_string_list "ana.activated") in if threadflag_active then let sens = GobConfig.get_string_list "ana.ctx_sens" in @@ -71,7 +71,10 @@ struct | sens -> (* use values of "ana.ctx_sens" (whitelist) *) List.mem "threadflag" sens in - not threadflag_ctx_sens + if not threadflag_ctx_sens then + true + else + ask.f (Queries.GasExhausted) else true end diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 71c887dda5..e4c0e261e4 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -318,6 +318,13 @@ struct f (Result.top ()) (!base_id, spec !base_id, assoc !base_id ctx.local) *) | Queries.DYojson -> `Lifted (D.to_yojson ctx.local) + | Queries.GasExhausted -> + if (get_int "ana.context.gas_value" >= 0) then + (* There is a lifter above this that will answer it, save to ask *) + ctx.ask (Queries.GasExhausted) + else + (* Abort to avoid infinite recursion *) + false | _ -> let r = fold_left (f ~q) (Result.top ()) @@ spec_list ctx.local in do_sideg ctx !sides; diff --git a/src/domains/queries.ml b/src/domains/queries.ml index a904f696eb..1ceb91ef1d 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -126,6 +126,7 @@ type _ t = | IsEverMultiThreaded: MayBool.t t | TmpSpecial: Mval.Exp.t -> ML.t t | MaySignedOverflow: exp -> MayBool.t t + | GasExhausted: MustBool.t t type 'a result = 'a @@ -196,6 +197,7 @@ struct | IsEverMultiThreaded -> (module MayBool) | TmpSpecial _ -> (module ML) | MaySignedOverflow _ -> (module MayBool) + | GasExhausted -> (module MustBool) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -265,6 +267,7 @@ struct | IsEverMultiThreaded -> MayBool.top () | TmpSpecial _ -> ML.top () | MaySignedOverflow _ -> MayBool.top () + | GasExhausted -> MustBool.top () end (* The type any_query can't be directly defined in Any as t, @@ -331,6 +334,7 @@ struct | Any (TmpSpecial _) -> 56 | Any (IsAllocVar _) -> 57 | Any (MaySignedOverflow _) -> 58 + | Any GasExhausted -> 59 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -490,6 +494,7 @@ struct | Any IsEverMultiThreaded -> Pretty.dprintf "IsEverMultiThreaded" | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv | Any (MaySignedOverflow e) -> Pretty.dprintf "MaySignedOverflow %a" CilType.Exp.pretty e + | Any GasExhausted -> Pretty.dprintf "GasExhausted" end let to_value_domain_ask (ask: ask) = diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 6a1eae3ed1..e0e36801ed 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -575,8 +575,14 @@ struct let liftmap f = List.map (fun (x) -> (x, max 0 (cg_val ctx - 1))) f in liftmap (S.threadenter (conv ctx) ~multiple lval f args) + let query ctx (type a) (q: a Queries.t):a Queries.result = + match q with + | Queries.GasExhausted -> + let (d,i) = ctx.local in + (i <= 0) + | _ -> S.query (conv ctx) q + let sync ctx reason = S.sync (conv ctx) reason, cg_val ctx - let query ctx q = S.query (conv ctx) q let assign ctx lval expr = S.assign (conv ctx) lval expr, cg_val ctx let vdecl ctx v = S.vdecl (conv ctx) v, cg_val ctx let body ctx fundec = S.body (conv ctx) fundec, cg_val ctx diff --git a/tests/regression/80-context_gas/21-sync.c b/tests/regression/80-context_gas/21-sync.c index 5769ffaa67..33edf254e5 100644 --- a/tests/regression/80-context_gas/21-sync.c +++ b/tests/regression/80-context_gas/21-sync.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.context.gas_value 3 +// PARAM: --set ana.context.gas_value 1 // Like 00/35 but with gas this time! // Misbehaves for gas <= 3 #include diff --git a/tests/regression/80-context_gas/22-sync-precision.c b/tests/regression/80-context_gas/22-sync-precision.c new file mode 100644 index 0000000000..8fc1dd7779 --- /dev/null +++ b/tests/regression/80-context_gas/22-sync-precision.c @@ -0,0 +1,36 @@ +// PARAM: --set ana.context.gas_value 4 +// Like 00/35 but with gas this time! +// Misbehaves for gas <= 3 +#include +#include + +int g = 1; + +void foo() { + // Check that we don't lose precision due to JoinCall + int x = g; + int x2 = g; + // A hack: In both contexts g has a single possible value, so we check that x = x2 + // to verify there is no precision loss + + __goblint_check(x == x2); +} + +void *t_fun(void *arg) { + foo(); +} + +int do_stuff() { + foo(); + g = 2; + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + return 0; +} + +int main() { + do_stuff(); + return 0; +} From 2e915afaac498209620aee7fc0f5e547d2f7d300 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 13 Jun 2024 11:09:03 +0300 Subject: [PATCH 209/566] Enable pthreadMutexType analysis by default --- conf/examples/medium-program.json | 2 ++ conf/examples/very-precise.json | 2 ++ src/config/options.schema.json | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/conf/examples/medium-program.json b/conf/examples/medium-program.json index 2c1e7c7346..5afc1aa2f8 100644 --- a/conf/examples/medium-program.json +++ b/conf/examples/medium-program.json @@ -9,6 +9,7 @@ "base", "threadid", "threadflag", + "threadreturn", "mallocWrapper", "mutexEvents", "mutex", @@ -18,6 +19,7 @@ "expRelation", "mhp", "assert", + "pthreadMutexType", "var_eq", "symb_locks", "region", diff --git a/conf/examples/very-precise.json b/conf/examples/very-precise.json index 2197335eaf..074d448a85 100644 --- a/conf/examples/very-precise.json +++ b/conf/examples/very-precise.json @@ -22,6 +22,7 @@ "base", "threadid", "threadflag", + "threadreturn", "mallocWrapper", "mutexEvents", "mutex", @@ -31,6 +32,7 @@ "expRelation", "mhp", "assert", + "pthreadMutexType", "var_eq", "symb_locks", "region", diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 488fc494b0..7068cce719 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -344,7 +344,7 @@ "default": [ "expRelation", "base", "threadid", "threadflag", "threadreturn", "escape", "mutexEvents", "mutex", "access", "race", "mallocWrapper", "mhp", - "assert" + "assert", "pthreadMutexType" ] }, "path_sens": { From 5962aedde36fa0caadaf77123ae6bf80b90ecc28 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 13 Jun 2024 11:09:48 +0300 Subject: [PATCH 210/566] Add pthreadMutexType to single-threaded autotuner --- src/autoTune.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 434b4fb0b2..8ec77739e7 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -186,7 +186,7 @@ let enableAnalyses anas = (*escape is also still enabled, because otherwise we get a warning*) (*does not consider dynamic calls!*) -let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"; "mhp"; "region"] +let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"; "mhp"; "region"; "pthreadMutexType"] let reduceThreadAnalyses () = let isThreadCreate = function | LibraryDesc.ThreadCreate _ -> true From 30aeb975856b1fe4686bd82648cbd43c15ce686c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 13 Jun 2024 11:10:19 +0300 Subject: [PATCH 211/566] Use Mval.Unit for MutexTypeAnalysis.V --- src/analyses/mutexTypeAnalysis.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 441f2e9953..e8edddd41e 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -15,8 +15,9 @@ struct (* Removing indexes here avoids complicated lookups and allows to have the LVals as vars here, at the price that different types of mutexes in arrays are not dinstinguished *) module O = Offset.Unit - module V = struct - include Printable.Prod(CilType.Varinfo)(O) (* TODO: use Mval.Unit *) + module V = + struct + include Mval.Unit let is_write_only _ = false end From 10fe38ecd5eab38d0a567e4551e99928508ca0b9 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 13 Jun 2024 11:10:48 +0300 Subject: [PATCH 212/566] Binary operations on pointer types should not generate overflow warnings in SV-COMP --- src/analyses/base.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index b80ac59d7f..6f768cc1d1 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -885,7 +885,11 @@ struct | None -> evalbinop ~ctx st LOr ~e1 ~e2 typ (* fallback to general case *) end | BinOp (op,e1,e2,typ) -> - evalbinop ~ctx st op ~e1 ~e2 typ + begin match typ with + | TPtr _ -> (* Binary operations on pointer types should not generate warnings in SV-COMP *) + GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> evalbinop ~ctx st op ~e1 ~e2 typ + | _ -> evalbinop ~ctx st op ~e1 ~e2 typ + end (* Unary operators *) | UnOp (op,arg1,typ) -> let a1 = eval_rv ~ctx st arg1 in From 578ca314fd3831f5dce193b0c6df49f60e47904b Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 13 Jun 2024 12:03:16 +0300 Subject: [PATCH 213/566] Revert "Binary operations on pointer types should not generate overflow warnings in SV-COMP" This reverts commit 10fe38ecd5eab38d0a567e4551e99928508ca0b9. --- src/analyses/base.ml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 6f768cc1d1..b80ac59d7f 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -885,11 +885,7 @@ struct | None -> evalbinop ~ctx st LOr ~e1 ~e2 typ (* fallback to general case *) end | BinOp (op,e1,e2,typ) -> - begin match typ with - | TPtr _ -> (* Binary operations on pointer types should not generate warnings in SV-COMP *) - GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> evalbinop ~ctx st op ~e1 ~e2 typ - | _ -> evalbinop ~ctx st op ~e1 ~e2 typ - end + evalbinop ~ctx st op ~e1 ~e2 typ (* Unary operators *) | UnOp (op,arg1,typ) -> let a1 = eval_rv ~ctx st arg1 in From c818f0fd92cada2785aa3659ef5961dcc7036fc6 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 13 Jun 2024 12:08:01 +0300 Subject: [PATCH 214/566] Binary operations on pointer types should not generate overflow warnings in SV-COMP --- src/analyses/base.ml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index b80ac59d7f..2e89f98bb2 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -261,6 +261,8 @@ struct (* adds n to the last offset *) let rec addToOffset n (t:typ option) = function | `Index (i, `NoOffset) -> + (* Binary operations on pointer types should not generate warnings in SV-COMP *) + GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> (* If we have arrived at the last Offset and it is an Index, we add our integer to it *) `Index(IdxDom.add i (iDtoIdx n), `NoOffset) | `Field (f, `NoOffset) -> From eec5f2c747d6afae162501a0a94f28637f5d2f81 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 13 Jun 2024 14:49:28 +0200 Subject: [PATCH 215/566] Rename test and add reference --- tests/regression/46-apron2/{89-sides-pp.c => 89-flag-ctx-sens.c} | 1 + 1 file changed, 1 insertion(+) rename tests/regression/46-apron2/{89-sides-pp.c => 89-flag-ctx-sens.c} (89%) diff --git a/tests/regression/46-apron2/89-sides-pp.c b/tests/regression/46-apron2/89-flag-ctx-sens.c similarity index 89% rename from tests/regression/46-apron2/89-sides-pp.c rename to tests/regression/46-apron2/89-flag-ctx-sens.c index 10fc5f8d1e..41e4f02520 100644 --- a/tests/regression/46-apron2/89-sides-pp.c +++ b/tests/regression/46-apron2/89-flag-ctx-sens.c @@ -1,5 +1,6 @@ // SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet // NOTIMEOUT +// See https://github.com/goblint/analyzer/pull/1508 #include #include From a3b436284771cea0e1d86bda1a5b32da12f5e849 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 13 Jun 2024 18:04:33 +0300 Subject: [PATCH 216/566] Add YAML witness violation_sequence entry type --- src/witness/yamlWitnessType.ml | 200 +++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 1630e05b69..526d52d327 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -426,6 +426,200 @@ struct let entry_type = "precondition_loop_invariant_certificate" end +module ViolationSequence = +struct + + module Constraint = + struct + type t = { + value: string; + format: string; + } + [@@deriving ord] + + let to_yaml {value; format} = + `O [ + ("value", `String value); + ("format", `String format); + ] + + let of_yaml y = + let open GobYaml in + let+ value = y |> find "value" >>= to_string + and+ format = y |> find "format" >>= to_string in + {value; format} + end + + module Assumption = + struct + type t = { + location: Location.t; + action: string; + constraint_: Constraint.t; + } + [@@deriving ord] + + let waypoint_type = "assumption" + + let to_yaml' {location; action; constraint_} = + [ + ("location", Location.to_yaml location); + ("action", `String action); + ("constraint", Constraint.to_yaml constraint_); + ] + + let of_yaml y = + let open GobYaml in + let+ location = y |> find "location" >>= Location.of_yaml + and+ action = y |> find "action" >>= to_string + and+ constraint_ = y |> find "constraint" >>= Constraint.of_yaml in + {location; action; constraint_} + end + + module Target = + struct + type t = { + location: Location.t; + action: string; + } + [@@deriving ord] + + let waypoint_type = "target" + + let to_yaml' {location; action} = + [ + ("location", Location.to_yaml location); + ("action", `String action); + ] + + let of_yaml y = + let open GobYaml in + let+ location = y |> find "location" >>= Location.of_yaml + and+ action = y |> find "action" >>= to_string in + {location; action} + end + + module FunctionEnter = + struct + include Target + + let waypoint_type = "function_enter" + end + + module FunctionReturn = + struct + include Assumption + + let waypoint_type = "function_return" + end + + module Branching = + struct + include Assumption + + let waypoint_type = "branching" + end + + (* TODO: could maybe use GADT, but adds ugly existential layer to entry type pattern matching *) + module WaypointType = + struct + type t = + | Assumption of Assumption.t + | Target of Target.t + | FunctionEnter of FunctionEnter.t + | FunctionReturn of FunctionReturn.t + | Branching of Branching.t + [@@deriving ord] + + let waypoint_type = function + | Assumption _ -> Assumption.waypoint_type + | Target _ -> Target.waypoint_type + | FunctionEnter _ -> FunctionEnter.waypoint_type + | FunctionReturn _ -> FunctionReturn.waypoint_type + | Branching _ -> Branching.waypoint_type + + let to_yaml' = function + | Assumption x -> Assumption.to_yaml' x + | Target x -> Target.to_yaml' x + | FunctionEnter x -> FunctionEnter.to_yaml' x + | FunctionReturn x -> FunctionReturn.to_yaml' x + | Branching x -> Branching.to_yaml' x + + let of_yaml y = + let open GobYaml in + let* waypoint_type = y |> find "type" >>= to_string in + if waypoint_type = Assumption.waypoint_type then + let+ x = y |> Assumption.of_yaml in + Assumption x + else if waypoint_type = Target.waypoint_type then + let+ x = y |> Target.of_yaml in + Target x + else if waypoint_type = FunctionEnter.waypoint_type then + let+ x = y |> FunctionEnter.of_yaml in + FunctionEnter x + else if waypoint_type = FunctionReturn.waypoint_type then + let+ x = y |> FunctionReturn.of_yaml in + FunctionReturn x + else if waypoint_type = Branching.waypoint_type then + let+ x = y |> Branching.of_yaml in + Branching x + else + Error (`Msg "type") + end + + module Waypoint = + struct + type t = { + waypoint_type: WaypointType.t; + } + [@@deriving ord] + + let to_yaml {waypoint_type} = + `O [ + ("waypoint", `O ([ + ("type", `String (WaypointType.waypoint_type waypoint_type)); + ] @ WaypointType.to_yaml' waypoint_type) + ) + ] + + let of_yaml y = + let open GobYaml in + let+ waypoint_type = y |> find "waypoint" >>= WaypointType.of_yaml in + {waypoint_type} + end + + module Segment = + struct + type t = { + segment: Waypoint.t list; + } + [@@deriving ord] + + let to_yaml {segment} = + `O [("segment", `A (List.map Waypoint.to_yaml segment))] + + let of_yaml y = + let open GobYaml in + let+ segment = y |> find "segment" >>= list >>= list_map Waypoint.of_yaml in + {segment} + end + + type t = { + content: Segment.t list; + } + [@@deriving ord] + + let entry_type = "violation_sequence" + + let to_yaml' {content} = + [("content", `A (List.map Segment.to_yaml content))] + + let of_yaml y = + let open GobYaml in + let+ content = y |> find "content" >>= list >>= list_map Segment.of_yaml in + {content} +end + (* TODO: could maybe use GADT, but adds ugly existential layer to entry type pattern matching *) module EntryType = struct @@ -437,6 +631,7 @@ struct | LoopInvariantCertificate of LoopInvariantCertificate.t | PreconditionLoopInvariantCertificate of PreconditionLoopInvariantCertificate.t | InvariantSet of InvariantSet.t + | ViolationSequence of ViolationSequence.t [@@deriving ord] let entry_type = function @@ -447,6 +642,7 @@ struct | LoopInvariantCertificate _ -> LoopInvariantCertificate.entry_type | PreconditionLoopInvariantCertificate _ -> PreconditionLoopInvariantCertificate.entry_type | InvariantSet _ -> InvariantSet.entry_type + | ViolationSequence _ -> ViolationSequence.entry_type let to_yaml' = function | LocationInvariant x -> LocationInvariant.to_yaml' x @@ -456,6 +652,7 @@ struct | LoopInvariantCertificate x -> LoopInvariantCertificate.to_yaml' x | PreconditionLoopInvariantCertificate x -> PreconditionLoopInvariantCertificate.to_yaml' x | InvariantSet x -> InvariantSet.to_yaml' x + | ViolationSequence x -> ViolationSequence.to_yaml' x let of_yaml y = let open GobYaml in @@ -481,6 +678,9 @@ struct else if entry_type = InvariantSet.entry_type then let+ x = y |> InvariantSet.of_yaml in InvariantSet x + else if entry_type = ViolationSequence.entry_type then + let+ x = y |> ViolationSequence.of_yaml in + ViolationSequence x else Error (`Msg "entry_type") end From a98da9d0c838a6073aff78515de848fb9d01047e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 14 Jun 2024 12:19:38 +0300 Subject: [PATCH 217/566] Implement violation_sequence in yamlWitnessStrip --- tests/util/yamlWitnessStrip.ml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/util/yamlWitnessStrip.ml b/tests/util/yamlWitnessStrip.ml index 8a5046d6ff..75c6cf3583 100644 --- a/tests/util/yamlWitnessStrip.ml +++ b/tests/util/yamlWitnessStrip.ml @@ -26,6 +26,25 @@ struct in {invariant_type} in + let waypoint_strip_file_hash ({waypoint_type}: ViolationSequence.Waypoint.t): ViolationSequence.Waypoint.t = + let waypoint_type: ViolationSequence.WaypointType.t = + match waypoint_type with + | Assumption x -> + Assumption {x with location = location_strip_file_hash x.location} + | Target x -> + Target {x with location = location_strip_file_hash x.location} + | FunctionEnter x -> + FunctionEnter {x with location = location_strip_file_hash x.location} + | FunctionReturn x -> + FunctionReturn {x with location = location_strip_file_hash x.location} + | Branching x -> + Branching {x with location = location_strip_file_hash x.location} + in + {waypoint_type} + in + let segment_strip_file_hash ({segment}: ViolationSequence.Segment.t): ViolationSequence.Segment.t = + {segment = List.map waypoint_strip_file_hash segment} + in let entry_type: EntryType.t = match entry_type with | LocationInvariant x -> @@ -42,6 +61,8 @@ struct PreconditionLoopInvariantCertificate {x with target = target_strip_file_hash x.target} | InvariantSet x -> InvariantSet {content = List.map invariant_strip_file_hash x.content} + | ViolationSequence x -> + ViolationSequence {content = List.map segment_strip_file_hash x.content} in {entry_type} From c4c4b258463442305d5837e08a6ef493a2afd304 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 14 Jun 2024 12:59:06 +0300 Subject: [PATCH 218/566] Add test for loop unrolling causing a signed integer overflow warning to appear --- .../55-loop-unrolling/12-loop-no-overflows.c | 37 ++++++++++ .../13-unrolled-loop-no-overflows.c | 72 +++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 tests/regression/55-loop-unrolling/12-loop-no-overflows.c create mode 100644 tests/regression/55-loop-unrolling/13-unrolled-loop-no-overflows.c diff --git a/tests/regression/55-loop-unrolling/12-loop-no-overflows.c b/tests/regression/55-loop-unrolling/12-loop-no-overflows.c new file mode 100644 index 0000000000..03554e06c5 --- /dev/null +++ b/tests/regression/55-loop-unrolling/12-loop-no-overflows.c @@ -0,0 +1,37 @@ +// PARAM: --enable ana.int.interval_set +// extracted from SV-COMP task ldv-memsafety/memleaks_test12-2.i + +typedef unsigned int size_t; +struct ldv_list_head { + struct ldv_list_head *next, *prev; +}; +struct ldv_list_head ldv_global_msg_list = {&(ldv_global_msg_list), &(ldv_global_msg_list)}; +struct ldv_msg { + void *data; + struct ldv_list_head list; +}; + +static inline void __ldv_list_del(struct ldv_list_head *prev, struct ldv_list_head *next) { + next->prev = prev; + prev->next = next; +} + +static inline void ldv_list_del(struct ldv_list_head *entry) { + __ldv_list_del(entry->prev, entry->next); +} + +void ldv_msg_free(struct ldv_msg *msg) { + free(msg->data); + free(msg); +} + +// ldv_destroy_msgs +void main(void) { + struct ldv_msg *msg; + struct ldv_msg *n; + for (msg = ({ const typeof( ((typeof(*msg) *)0)->list ) *__mptr = ((&ldv_global_msg_list)->next); (typeof(*msg) *)( (char *)__mptr - ((size_t) &((typeof(*msg) *)0)->list) ); }), n = ({ const typeof( ((typeof(*(msg)) *)0)->list ) *__mptr = ((msg)->list.next); (typeof(*(msg)) *)( (char *)__mptr - ((size_t) &((typeof(*(msg)) *)0)->list) ); }); &msg->list != (&ldv_global_msg_list); msg = n, n = ({ const typeof( ((typeof(*(n)) *)0)->list ) *__mptr = ((n)->list.next); (typeof(*(n)) *)( (char *)__mptr - ((size_t) &((typeof(*(n)) *)0)->list) ); })) // NOWARN + { + ldv_list_del(&msg->list); + ldv_msg_free(msg); + } +} \ No newline at end of file diff --git a/tests/regression/55-loop-unrolling/13-unrolled-loop-no-overflows.c b/tests/regression/55-loop-unrolling/13-unrolled-loop-no-overflows.c new file mode 100644 index 0000000000..84700ac9da --- /dev/null +++ b/tests/regression/55-loop-unrolling/13-unrolled-loop-no-overflows.c @@ -0,0 +1,72 @@ +// PARAM: --enable ana.int.interval_set +// extracted from SV-COMP task ldv-memsafety/memleaks_test12-2.i with --set exp.unrolling-factor 1 + +typedef unsigned int size_t; +struct ldv_list_head { + struct ldv_list_head *next, *prev; +}; +struct ldv_list_head ldv_global_msg_list = {&(ldv_global_msg_list), &(ldv_global_msg_list)}; +struct ldv_msg { + void *data; + struct ldv_list_head list; +}; + +__inline static void __ldv_list_del(struct ldv_list_head *prev, struct ldv_list_head *next) { + next->prev = prev; + prev->next = next; + return; +} + +__inline static void ldv_list_del(struct ldv_list_head *entry) { + __ldv_list_del(entry->prev, entry->next); + return; +} + +void ldv_msg_free(struct ldv_msg *msg) { + free(msg->data); + free((void *)msg); + return; +} + +// ldv_destroy_msgs +void main(void) { + struct ldv_msg *msg; + struct ldv_msg *n; + struct ldv_list_head const *__mptr; + struct ldv_list_head const *__mptr___0; + struct ldv_list_head const *__mptr___1; + + __mptr = (struct ldv_list_head const *)ldv_global_msg_list.next; + msg = (struct ldv_msg *)((char *)__mptr - (size_t)(&((struct ldv_msg *)0)->list)); + __mptr___0 = (struct ldv_list_head const *)msg->list.next; + n = (struct ldv_msg *)((char *)__mptr___0 - (size_t)(&((struct ldv_msg *)0)->list)); + + if (!((unsigned long)(&msg->list) != (unsigned long)(&ldv_global_msg_list))) { // NOWARN + goto loop_end; + } + + ldv_list_del(&msg->list); + ldv_msg_free(msg); + msg = n; + __mptr___1 = (struct ldv_list_head const *)n->list.next; + n = (struct ldv_msg *)((char *)__mptr___1 - (size_t)(&((struct ldv_msg *)0)->list)); + + loop_continue_0: /* CIL Label */; + { + while (1) { + while_continue: /* CIL Label */; + if (!((unsigned long)(&msg->list) != (unsigned long)(&ldv_global_msg_list))) { + goto while_break; + } + + ldv_list_del(&msg->list); + ldv_msg_free(msg); + msg = n; + __mptr___1 = (struct ldv_list_head const *)n->list.next; + n = (struct ldv_msg *)((char *)__mptr___1 - (size_t)(&((struct ldv_msg *)0)->list)); + } + while_break: /* CIL Label */; + } + loop_end: /* CIL Label */; + return; +} \ No newline at end of file From 44600c993976793a79c89177c68abc3b543a863d Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 14 Jun 2024 13:04:05 +0300 Subject: [PATCH 219/566] Binops on offsets should not generate overflow warnings in SV-COMP --- src/cdomain/value/cdomains/offset.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/offset.ml b/src/cdomain/value/cdomains/offset.ml index 9c9c5c3333..fa64f71daa 100644 --- a/src/cdomain/value/cdomains/offset.ml +++ b/src/cdomain/value/cdomains/offset.ml @@ -223,9 +223,10 @@ struct | _ -> (None, Idx.top ()) in - let bits_offset = Idx.mul item_size_in_bits x in + (* Binary operations on offsets should not generate overflow warnings in SV-COMP *) + let bits_offset = GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> Idx.mul item_size_in_bits x in let remaining_offset = offset_to_index_offset ?typ:item_typ o in - Idx.add bits_offset remaining_offset + GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> Idx.add bits_offset remaining_offset in offset_to_index_offset ?typ offs From e286a48decb04641f2e9bbe7fa1b96d1ba845293 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 14 Jun 2024 13:50:46 +0300 Subject: [PATCH 220/566] Make YAML witness location function optional In updated version 2.0 schema. --- src/witness/yamlWitness.ml | 2 +- src/witness/yamlWitnessType.ml | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 42254f30de..f5a8ae94e2 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -48,7 +48,7 @@ struct file_hash = sha256_file loc.file; line = loc.line; column = loc.column; - function_ = location_function; + function_ = Some location_function; } let invariant invariant: Invariant.t = { diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 1630e05b69..ff11fcbe9d 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -109,18 +109,23 @@ struct file_hash: string; line: int; column: int; - function_: string; + function_: string option; } [@@deriving ord] let to_yaml {file_name; file_hash; line; column; function_} = - `O [ - ("file_name", `String file_name); - ("file_hash", `String file_hash); - ("line", `Float (float_of_int line)); - ("column", `Float (float_of_int column)); - ("function", `String function_); - ] + `O ([ + ("file_name", `String file_name); + ("file_hash", `String file_hash); + ("line", `Float (float_of_int line)); + ("column", `Float (float_of_int column)); + ] @ match function_ with + | Some function_ -> [ + ("function", `String function_); + ] + | None -> + [] + ) let of_yaml y = let open GobYaml in @@ -128,7 +133,7 @@ struct and+ file_hash = y |> find "file_hash" >>= to_string and+ line = y |> find "line" >>= to_int and+ column = y |> find "column" >>= to_int - and+ function_ = y |> find "function" >>= to_string in + and+ function_ = y |> Yaml.Util.find "function" >>= option_map to_string in {file_name; file_hash; line; column; function_} end From 6a3569521a4dec529ceef50d82854db1ed350b0d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 14 Jun 2024 13:55:14 +0300 Subject: [PATCH 221/566] Make YAML witness location column optional In updated version 2.0 schema. --- src/analyses/unassumeAnalysis.ml | 22 +++++----------------- src/witness/yamlWitness.ml | 24 ++++++++++++------------ src/witness/yamlWitnessType.ml | 25 +++++++++++++++---------- 3 files changed, 32 insertions(+), 39 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 265e9c6925..ae7a8bc9a8 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -71,18 +71,6 @@ struct | _ -> () ); - let loc_of_location (location: YamlWitnessType.Location.t): Cil.location = { - file = location.file_name; - line = location.line; - column = location.column; - byte = -1; - endLine = -1; - endColumn = -1; - endByte = -1; - synthetic = false; - } - in - let yaml = match Yaml_unix.of_file (Fpath.v (GobConfig.get_string "witness.yaml.unassume")) with | Ok yaml -> yaml | Error (`Msg m) -> failwith ("Yaml_unix.of_file: " ^ m) @@ -123,7 +111,7 @@ struct in let unassume_location_invariant (location_invariant: YamlWitnessType.LocationInvariant.t) = - let loc = loc_of_location location_invariant.location in + let loc = YamlWitness.loc_of_location location_invariant.location in let inv = location_invariant.location_invariant.string in let msgLoc: M.Location.t = CilLocation loc in @@ -135,7 +123,7 @@ struct in let unassume_loop_invariant (loop_invariant: YamlWitnessType.LoopInvariant.t) = - let loc = loc_of_location loop_invariant.location in + let loc = YamlWitness.loc_of_location loop_invariant.location in let inv = loop_invariant.loop_invariant.string in let msgLoc: M.Location.t = CilLocation loc in @@ -185,7 +173,7 @@ struct in let unassume_precondition_loop_invariant (precondition_loop_invariant: YamlWitnessType.PreconditionLoopInvariant.t) = - let loc = loc_of_location precondition_loop_invariant.location in + let loc = YamlWitness.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 msgLoc: M.Location.t = CilLocation loc in @@ -200,7 +188,7 @@ struct let unassume_invariant_set (invariant_set: YamlWitnessType.InvariantSet.t) = let unassume_location_invariant (location_invariant: YamlWitnessType.InvariantSet.LocationInvariant.t) = - let loc = loc_of_location location_invariant.location in + let loc = YamlWitness.loc_of_location location_invariant.location in let inv = location_invariant.value in let msgLoc: M.Location.t = CilLocation loc in @@ -212,7 +200,7 @@ struct in let unassume_loop_invariant (loop_invariant: YamlWitnessType.InvariantSet.LoopInvariant.t) = - let loc = loc_of_location loop_invariant.location in + let loc = YamlWitness.loc_of_location loop_invariant.location in let inv = loop_invariant.value in let msgLoc: M.Location.t = CilLocation loc in diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index f5a8ae94e2..cf6b1dfc44 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -47,7 +47,7 @@ struct file_name = loc.file; file_hash = sha256_file loc.file; line = loc.line; - column = loc.column; + column = Some loc.column; function_ = Some location_function; } @@ -515,6 +515,17 @@ struct end +let loc_of_location (location: YamlWitnessType.Location.t): Cil.location = { + file = location.file_name; + line = location.line; + column = Option.value location.column ~default:1; + byte = -1; + endLine = -1; + endColumn = -1; + endByte = -1; + synthetic = false; +} + module ValidationResult = struct (* constructor order is important for the chain lattice *) @@ -553,17 +564,6 @@ struct module InvariantParser = WitnessUtil.InvariantParser module VR = ValidationResult - let loc_of_location (location: YamlWitnessType.Location.t): Cil.location = { - file = location.file_name; - line = location.line; - column = location.column; - byte = -1; - endLine = -1; - endColumn = -1; - endByte = -1; - synthetic = false; - } - let validate () = let location_locator = Locator.create () in let loop_locator = Locator.create () in diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index ff11fcbe9d..23a0343c18 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -108,7 +108,7 @@ struct file_name: string; file_hash: string; line: int; - column: int; + column: int option; function_: string option; } [@@deriving ord] @@ -118,21 +118,26 @@ struct ("file_name", `String file_name); ("file_hash", `String file_hash); ("line", `Float (float_of_int line)); - ("column", `Float (float_of_int column)); - ] @ match function_ with - | Some function_ -> [ - ("function", `String function_); - ] - | None -> - [] - ) + ] @ (match column with + | Some column -> [ + ("column", `Float (float_of_int column)); + ] + | None -> + [] + ) @ (match function_ with + | Some function_ -> [ + ("function", `String function_); + ] + | None -> + [] + )) let of_yaml y = let open GobYaml in let+ file_name = y |> find "file_name" >>= to_string and+ file_hash = y |> find "file_hash" >>= to_string and+ line = y |> find "line" >>= to_int - and+ column = y |> find "column" >>= to_int + and+ column = y |> Yaml.Util.find "column" >>= option_map to_int and+ function_ = y |> Yaml.Util.find "function" >>= option_map to_string in {file_name; file_hash; line; column; function_} end From b3dd140985c1db0e086bb42b51f5d5a6a045fc92 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 14 Jun 2024 14:03:04 +0300 Subject: [PATCH 222/566] Make YAML witness location file_hash optional Removed from version 2.0 schema. --- src/witness/yamlWitness.ml | 21 ++++++++++++++------- src/witness/yamlWitnessType.ml | 16 +++++++++++----- tests/util/yamlWitnessStrip.ml | 9 +++++++-- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index cf6b1dfc44..d880663547 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -43,13 +43,20 @@ struct specification } - let location ~location:(loc: Cil.location) ~(location_function): Location.t = { - file_name = loc.file; - file_hash = sha256_file loc.file; - line = loc.line; - column = Some loc.column; - function_ = Some location_function; - } + let location ~location:(loc: Cil.location) ~(location_function): Location.t = + let file_hash = + match GobConfig.get_string "witness.yaml.format-version" with + | "0.1" -> Some (sha256_file loc.file) + | "2.0" -> None + | _ -> assert false + in + { + file_name = loc.file; + file_hash; + line = loc.line; + column = Some loc.column; + function_ = Some location_function; + } let invariant invariant: Invariant.t = { string = invariant; diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 23a0343c18..aabf551109 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -106,7 +106,7 @@ module Location = struct type t = { file_name: string; - file_hash: string; + file_hash: string option; line: int; column: int option; function_: string option; @@ -116,9 +116,15 @@ struct let to_yaml {file_name; file_hash; line; column; function_} = `O ([ ("file_name", `String file_name); - ("file_hash", `String file_hash); - ("line", `Float (float_of_int line)); - ] @ (match column with + ] @ (match file_hash with + | Some file_hash -> [ + ("file_hash", `String file_hash); + ] + | None -> + [] + ) @ [ + ("line", `Float (float_of_int line)); + ] @ (match column with | Some column -> [ ("column", `Float (float_of_int column)); ] @@ -135,7 +141,7 @@ struct let of_yaml y = let open GobYaml in let+ file_name = y |> find "file_name" >>= to_string - and+ file_hash = y |> find "file_hash" >>= to_string + and+ file_hash = y |> Yaml.Util.find "file_hash" >>= option_map to_string and+ line = y |> find "line" >>= to_int and+ column = y |> Yaml.Util.find "column" >>= option_map to_int and+ function_ = y |> Yaml.Util.find "function" >>= option_map to_string in diff --git a/tests/util/yamlWitnessStrip.ml b/tests/util/yamlWitnessStrip.ml index 8a5046d6ff..1a463d1390 100644 --- a/tests/util/yamlWitnessStrip.ml +++ b/tests/util/yamlWitnessStrip.ml @@ -10,8 +10,13 @@ struct let strip_file_hashes {entry_type} = let stripped_file_hash = "$FILE_HASH" in - let location_strip_file_hash location: Location.t = - {location with file_hash = stripped_file_hash} + let location_strip_file_hash (location: Location.t): Location.t = + let file_hash = + match location.file_hash with + | Some _ -> Some stripped_file_hash (* TODO: or just map to None always? *) + | None -> None + in + {location with file_hash} in let target_strip_file_hash target: Target.t = {target with file_hash = stripped_file_hash} From 28cff0f0917ecd5d2bd38972b6f0d66369b2d873 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 14 Jun 2024 14:10:45 +0300 Subject: [PATCH 223/566] Make YAML witness waypoint constraint format optional --- src/witness/yamlWitnessType.ml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 159b0eedae..4fc2029801 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -449,20 +449,25 @@ struct struct type t = { value: string; - format: string; + format: string option; } [@@deriving ord] let to_yaml {value; format} = - `O [ - ("value", `String value); - ("format", `String format); - ] + `O ([ + ("value", `String value); + ] @ (match format with + | Some format -> [ + ("format", `String format); + ] + | None -> + [] + )) let of_yaml y = let open GobYaml in let+ value = y |> find "value" >>= to_string - and+ format = y |> find "format" >>= to_string in + and+ format = y |> Yaml.Util.find "format" >>= option_map to_string in {value; format} end @@ -593,8 +598,8 @@ struct let to_yaml {waypoint_type} = `O [ ("waypoint", `O ([ - ("type", `String (WaypointType.waypoint_type waypoint_type)); - ] @ WaypointType.to_yaml' waypoint_type) + ("type", `String (WaypointType.waypoint_type waypoint_type)); + ] @ WaypointType.to_yaml' waypoint_type) ) ] From 1bdb1aec3d0de987c83a3e3ff5ab0514bc965772 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 14 Jun 2024 14:58:32 +0300 Subject: [PATCH 224/566] Add primitive YAML violation_sequence refutation (issue #1301) --- src/config/options.schema.json | 3 ++- src/witness/yamlWitness.ml | 13 ++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 488fc494b0..95fb1a9ff5 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -2574,7 +2574,8 @@ "precondition_loop_invariant", "loop_invariant_certificate", "precondition_loop_invariant_certificate", - "invariant_set" + "invariant_set", + "violation_sequence" ] }, "default": [ diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index d880663547..36e1e2b589 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -802,6 +802,15 @@ struct None in + let validate_violation_sequence (violation_sequence: YamlWitnessType.ViolationSequence.t) = + (* TODO: update cnt-s appropriately (needs access to SV-COMP result pre-witness validation) *) + (* Nothing needs to be checked here! + If program is correct and we can prove it, we output true, which counts as refutation of violation witness. + If program is correct and we cannot prove it, we output unknown. + If program is incorrect, we output unknown. *) + None + in + match entry_type_enabled target_type, entry.entry_type with | true, LocationInvariant x -> validate_location_invariant x @@ -811,7 +820,9 @@ struct validate_precondition_loop_invariant x | true, InvariantSet x -> validate_invariant_set x - | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _ | InvariantSet _) -> + | true, ViolationSequence x -> + validate_violation_sequence x + | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _ | InvariantSet _ | ViolationSequence _) -> incr cnt_disabled; M.info_noloc ~category:Witness "disabled entry of type %s" target_type; None From c546f2df8208ac3ff22886e65f64f3eb84c8e044 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 14 Jun 2024 15:00:10 +0300 Subject: [PATCH 225/566] Add svcomp-validate conf for next SV-COMP Compared to svcomp24-validate: 1. Enables abortUnless (like svcomp over svcomp24). 2. Enables YAML violation_sequence validation. --- conf/svcomp-validate.json | 142 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 conf/svcomp-validate.json diff --git a/conf/svcomp-validate.json b/conf/svcomp-validate.json new file mode 100644 index 0000000000..a234aeb0d5 --- /dev/null +++ b/conf/svcomp-validate.json @@ -0,0 +1,142 @@ +{ + "ana": { + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "float": { + "interval": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "race", + "escape", + "expRelation", + "mhp", + "assert", + "var_eq", + "symb_locks", + "region", + "thread", + "threadJoins", + "abortUnless", + "unassume" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "memLeak", + "threadflag" + ], + "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", + "ldv_kzalloc" + ] + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "octagon", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" + ] + }, + "widen": { + "tokens": true + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": false + }, + "yaml": { + "enabled": false, + "strict": true, + "format-version": "2.0", + "entry-types": [ + "location_invariant", + "loop_invariant", + "invariant_set", + "violation_sequence" + ], + "invariant-types": [ + "location_invariant", + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": true, + "other": true + } + }, + "pre": { + "enabled": false + } +} From fccc91efee6c2dc0ad92c3d82946e1b1e994d7d8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 14 Jun 2024 15:57:34 +0300 Subject: [PATCH 226/566] Add cram test for YAML violation witness refutation --- .../witness/violation.t/correct-hard.c | 9 +++ .../witness/violation.t/correct-hard.yml | 26 +++++++++ .../regression/witness/violation.t/correct.c | 5 ++ .../witness/violation.t/correct.yml | 26 +++++++++ .../witness/violation.t/incorrect.c | 6 ++ .../witness/violation.t/incorrect.yml | 26 +++++++++ tests/regression/witness/violation.t/run.t | 57 +++++++++++++++++++ 7 files changed, 155 insertions(+) create mode 100644 tests/regression/witness/violation.t/correct-hard.c create mode 100644 tests/regression/witness/violation.t/correct-hard.yml create mode 100644 tests/regression/witness/violation.t/correct.c create mode 100644 tests/regression/witness/violation.t/correct.yml create mode 100644 tests/regression/witness/violation.t/incorrect.c create mode 100644 tests/regression/witness/violation.t/incorrect.yml create mode 100644 tests/regression/witness/violation.t/run.t diff --git a/tests/regression/witness/violation.t/correct-hard.c b/tests/regression/witness/violation.t/correct-hard.c new file mode 100644 index 0000000000..ce2b50a0c0 --- /dev/null +++ b/tests/regression/witness/violation.t/correct-hard.c @@ -0,0 +1,9 @@ +void reach_error(){} +extern int __VERIFIER_nondet_int(); + +int main() { + int x = __VERIFIER_nondet_int(); + if (x != x) + reach_error(); + return 0; +} diff --git a/tests/regression/witness/violation.t/correct-hard.yml b/tests/regression/witness/violation.t/correct-hard.yml new file mode 100644 index 0000000000..b8af2c2193 --- /dev/null +++ b/tests/regression/witness/violation.t/correct-hard.yml @@ -0,0 +1,26 @@ +- entry_type: violation_sequence + metadata: + format_version: "2.0" + uuid: 4412af70-389a-475e-849c-e57e5b92019e + creation_time: 2024-06-14T15:35:00+03:00 + producer: + name: Simmo Saan + version: n/a + task: + input_files: + - correct-hard.c + input_file_hashes: + correct-hard.c: 5cc49c1ce5a9aef64286c2a6e57f6955c5f4f9b19b43056507ae87a802802447 + specification: G ! call(reach_error()) + data_model: ILP32 + language: C + content: + - segment: + - waypoint: + type: target + action: follow + location: + file_name: correct-hard.c + line: 7 + column: 5 + function: main diff --git a/tests/regression/witness/violation.t/correct.c b/tests/regression/witness/violation.t/correct.c new file mode 100644 index 0000000000..30f58a2f7e --- /dev/null +++ b/tests/regression/witness/violation.t/correct.c @@ -0,0 +1,5 @@ +void reach_error(){} + +int main() { + return 0; +} diff --git a/tests/regression/witness/violation.t/correct.yml b/tests/regression/witness/violation.t/correct.yml new file mode 100644 index 0000000000..1f1d4f41da --- /dev/null +++ b/tests/regression/witness/violation.t/correct.yml @@ -0,0 +1,26 @@ +- entry_type: violation_sequence + metadata: + format_version: "2.0" + uuid: 4412af70-389a-475e-849c-e57e5b92019d + creation_time: 2024-06-14T15:35:00+03:00 + producer: + name: Simmo Saan + version: n/a + task: + input_files: + - correct.c + input_file_hashes: + correct.c: 6f760cf7f33fc152738bf3514fe623cc94e52cad9ddc2f0e744595ce0de07530 + specification: G ! call(reach_error()) + data_model: ILP32 + language: C + content: + - segment: + - waypoint: + type: target + action: follow + location: + file_name: correct.c + line: 4 + column: 3 + function: main diff --git a/tests/regression/witness/violation.t/incorrect.c b/tests/regression/witness/violation.t/incorrect.c new file mode 100644 index 0000000000..ff56fa2ef4 --- /dev/null +++ b/tests/regression/witness/violation.t/incorrect.c @@ -0,0 +1,6 @@ +void reach_error(){} + +int main() { + reach_error(); + return 0; +} diff --git a/tests/regression/witness/violation.t/incorrect.yml b/tests/regression/witness/violation.t/incorrect.yml new file mode 100644 index 0000000000..dd57ce3ca1 --- /dev/null +++ b/tests/regression/witness/violation.t/incorrect.yml @@ -0,0 +1,26 @@ +- entry_type: violation_sequence + metadata: + format_version: "2.0" + uuid: 4412af70-389a-475e-849c-e57e5b92019c + creation_time: 2024-06-14T15:35:00+03:00 + producer: + name: Simmo Saan + version: n/a + task: + input_files: + - incorrect.c + input_file_hashes: + incorrect.c: 1af4fd9e76418e4b95af9950b58248127e7c2d9eb791e1c9b92da53952e0fca2 + specification: G ! call(reach_error()) + data_model: ILP32 + language: C + content: + - segment: + - waypoint: + type: target + action: follow + location: + file_name: incorrect.c + line: 4 + column: 3 + function: main diff --git a/tests/regression/witness/violation.t/run.t b/tests/regression/witness/violation.t/run.t new file mode 100644 index 0000000000..1fc408635e --- /dev/null +++ b/tests/regression/witness/violation.t/run.t @@ -0,0 +1,57 @@ +Violation witness for a correct program can be refuted by proving the program correct and returning `true`: + + $ goblint --enable ana.sv-comp.enabled --set witness.yaml.entry-types[+] violation_sequence --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" correct.c --set witness.yaml.validate correct.yml + [Info] SV-COMP specification: CHECK( init(main()), LTL(G ! call(reach_error())) ) + [Warning][Deadcode] Function 'reach_error' is uncalled: 1 LLoC (correct.c:1:1-1:20) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 2 + dead: 1 (1 in uncalled functions) + total lines: 3 + [Info][Witness] witness validation summary: + confirmed: 0 + unconfirmed: 0 + refuted: 0 + error: 0 + unchecked: 0 + unsupported: 0 + disabled: 0 + total validation entries: 0 + SV-COMP result: true + +If a correct progtam cannot be proven correct, return `unknown` for the violation witness: + + $ goblint --set ana.activated[-] expRelation --enable ana.sv-comp.functions --enable ana.sv-comp.enabled --set witness.yaml.entry-types[+] violation_sequence --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" correct-hard.c --set witness.yaml.validate correct-hard.yml + [Info] SV-COMP specification: CHECK( init(main()), LTL(G ! call(reach_error())) ) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Witness] witness validation summary: + confirmed: 0 + unconfirmed: 0 + refuted: 0 + error: 0 + unchecked: 0 + unsupported: 0 + disabled: 0 + total validation entries: 0 + SV-COMP result: unknown + +Violation witness for an incorrect program cannot be proven correct, so return `unknown`: + + $ goblint --enable ana.sv-comp.enabled --set witness.yaml.entry-types[+] violation_sequence --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" incorrect.c --set witness.yaml.validate incorrect.yml + [Info] SV-COMP specification: CHECK( init(main()), LTL(G ! call(reach_error())) ) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 4 + dead: 0 + total lines: 4 + [Info][Witness] witness validation summary: + confirmed: 0 + unconfirmed: 0 + refuted: 0 + error: 0 + unchecked: 0 + unsupported: 0 + disabled: 0 + total validation entries: 0 + SV-COMP result: unknown From 8920200c71df4a855d22ef7f330eadad64aca205 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Mon, 17 Jun 2024 13:34:41 +0200 Subject: [PATCH 227/566] next try --- src/cdomains/apron/sharedFunctions.apron.ml | 53 ++++++++++++++------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 7c69cefda4..1bcc6d7202 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -6,15 +6,8 @@ open GobApron module M = Messages -let frac_of_scalar scalar = - if Scalar.is_infty scalar <> 0 then (* infinity means unbounded *) - None - else match scalar with - | Float f -> if Stdlib.Float.is_integer f then Some (Q.of_float f) else None - | Mpqf f -> Some (Z_mlgmpidl.q_of_mpqf f) - | _ -> failwith "frac_of_scalar: unsupported" -let int_of_scalar ?round (scalar: Scalar.t) = +let int_of_scalar ?(scalewith=Z.one) ?round (scalar: Scalar.t) = if Scalar.is_infty scalar <> 0 then (* infinity means unbounded *) None else @@ -28,18 +21,19 @@ let int_of_scalar ?round (scalar: Scalar.t) = | None when Stdlib.Float.is_integer f -> Some f | None -> None in - Z.of_float f + Z.(of_float f * scalewith) | Mpqf scalar -> (* octMPQ, boxMPQ, polkaMPQ *) let n = Mpqf.get_num scalar in let d = Mpqf.get_den scalar in + let scale = Z_mlgmpidl.mpz_of_z scalewith in let+ z = if Mpzf.cmp_int d 1 = 0 then (* exact integer (denominator 1) *) - Some n + Some (Mpzf.mul scale n) else begin match round with - | Some `Floor -> Some (Mpzf.fdiv_q n d) (* floor division *) - | Some `Ceil -> Some (Mpzf.cdiv_q n d) (* ceiling division *) - | None -> None + | Some `Floor -> Some (Mpzf.mul scale (Mpzf.fdiv_q n d)) (* floor division *) + | Some `Ceil -> Some (Mpzf.mul scale (Mpzf.cdiv_q n d)) (* ceiling division *) + | None -> Some (Mpzf.divexact (Mpzf.mul scale n ) d) (* scale, preferably with common denominator *) end in Z_mlgmpidl.z_of_mpzf z @@ -246,11 +240,11 @@ module CilOfApron (V: SV) = struct exception Unsupported_Linexpr1 - let cil_exp_of_linexpr1 (linexpr1:Linexpr1.t) = + let cil_exp_of_linexpr1 ?(scalewith=Z.one) (linexpr1:Linexpr1.t) = let longlong = TInt(ILongLong,[]) in let coeff_to_const consider_flip (c:Coeff.union_5) = match c with | Scalar c -> - (match int_of_scalar c with + (match int_of_scalar ~scalewith:scalewith c with | Some i -> let ci,truncation = truncateCilint ILongLong i in if truncation = NoTruncation then @@ -286,11 +280,38 @@ struct !expr + let lcm_den linexpr1 = + let exception UnsupportedScalar + in + let frac_of_scalar scalar = + if Scalar.is_infty scalar <> 0 then (* infinity means unbounded *) + None + else match scalar with + | Float f -> if Stdlib.Float.is_integer f then Some (Q.of_float f) else None + | Mpqf f -> Some (Z_mlgmpidl.q_of_mpqf f) + | _ -> raise UnsupportedScalar + in + let extract_den (c:Coeff.union_5) _ = + match c with + | Scalar c -> BatOption.map (fun q -> Q.den q) (frac_of_scalar c) + | _ -> None + in + let lcm_denom = ref (BatOption.default Z.one (extract_den (Linexpr1.get_cst linexpr1) ())) in + let lcm_coeff (c:Coeff.union_5) v = + match (extract_den c v) with + | Some z -> lcm_denom := Z.lcm z !lcm_denom + | _ -> () + in + try + Linexpr1.iter lcm_coeff linexpr1; !lcm_denom + with UnsupportedScalar -> Z.one + let cil_exp_of_lincons1 (lincons1:Lincons1.t) = let zero = Cil.kinteger ILongLong 0 in try let linexpr1 = Lincons1.get_linexpr1 lincons1 in - let cilexp = cil_exp_of_linexpr1 linexpr1 in + let common_denominator = lcm_den linexpr1 in + let cilexp = cil_exp_of_linexpr1 ~scalewith:common_denominator linexpr1 in match Lincons1.get_typ lincons1 with | EQ -> Some (Cil.constFold false @@ BinOp(Eq, cilexp, zero, TInt(IInt,[]))) | SUPEQ -> Some (Cil.constFold false @@ BinOp(Ge, cilexp, zero, TInt(IInt,[]))) From 2fefea01a7816968035328557d060cd5f61f5407 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 20:52:18 +0000 Subject: [PATCH 228/566] Bump docker/build-push-action from 5 to 6 Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v5...v6) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docker.yml | 4 ++-- .github/workflows/unlocked.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 36568e6cb2..daa7f224b8 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -59,7 +59,7 @@ jobs: - name: Build Docker image id: build - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . load: true # load into docker instead of immediately pushing @@ -72,7 +72,7 @@ jobs: run: docker run --rm -v $(pwd):/data ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} /data/tests/regression/04-mutex/01-simple_rc.c # run image by version in case multiple tags - name: Push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . push: true diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index db41ad3007..f1bd399a17 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -165,7 +165,7 @@ jobs: - name: Build dev Docker image id: build - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . target: dev From a7e4ff18c7a5f15d0bc03bcf9caa1b402d39b381 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 18 Jun 2024 10:57:24 +0200 Subject: [PATCH 229/566] Update src/cdomains/apron/sharedFunctions.apron.ml Co-authored-by: Simmo Saan --- src/cdomains/apron/sharedFunctions.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index f3cdf6f8ba..17fdb2175c 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -300,7 +300,7 @@ struct in let extract_den (c:Coeff.union_5) _ = match c with - | Scalar c -> BatOption.map (fun q -> Q.den q) (frac_of_scalar c) + | Scalar c -> BatOption.map Q.den (frac_of_scalar c) | _ -> None in let lcm_denom = ref (BatOption.default Z.one (extract_den (Linexpr1.get_cst linexpr1) ())) in From 7876d6b3c38f79c50d1361e62d8ab20f0d2e49fe Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 18 Jun 2024 10:58:06 +0200 Subject: [PATCH 230/566] Update src/cdomains/apron/sharedFunctions.apron.ml Co-authored-by: Simmo Saan --- src/cdomains/apron/sharedFunctions.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 17fdb2175c..656fe0b9bd 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -251,7 +251,7 @@ struct let longlong = TInt(ILongLong,[]) in let coeff_to_const consider_flip (c:Coeff.union_5) = match c with | Scalar c -> - (match int_of_scalar ~scalewith:scalewith c with + (match int_of_scalar ?scalewith c with | Some i -> let ci,truncation = truncateCilint ILongLong i in if truncation = NoTruncation then From e4cd4df9d9f3c6678596060c11efd058466d0f6d Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 18 Jun 2024 10:58:17 +0200 Subject: [PATCH 231/566] Update src/cdomains/apron/sharedFunctions.apron.ml Co-authored-by: Simmo Saan --- src/cdomains/apron/sharedFunctions.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 656fe0b9bd..022e60200d 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -247,7 +247,7 @@ module CilOfApron (V: SV) = struct exception Unsupported_Linexpr1 - let cil_exp_of_linexpr1 ?(scalewith=Z.one) (linexpr1:Linexpr1.t) = + let cil_exp_of_linexpr1 ?scalewith (linexpr1:Linexpr1.t) = let longlong = TInt(ILongLong,[]) in let coeff_to_const consider_flip (c:Coeff.union_5) = match c with | Scalar c -> From 4812b07e58b7c854e2fc4c25763101cb73800e69 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 18 Jun 2024 13:41:00 +0300 Subject: [PATCH 232/566] Avoid unused value warning on LibraryFunctions.all_library_descs --- src/util/library/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index 4e678c926e..e7ff2a4d04 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -1246,7 +1246,7 @@ let libraries = descs_tbl ) libraries -let all_library_descs: (string, LibraryDesc.t) Hashtbl.t = +let _all_library_descs: (string, LibraryDesc.t) Hashtbl.t = Hashtbl.fold (fun _ descs_tbl acc -> Hashtbl.merge (fun name desc1 desc2 -> match desc1, desc2 with From d5f67cb1d1c811a8b207e4095deebc7836d302e9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 18 Jun 2024 15:49:36 +0300 Subject: [PATCH 233/566] Fix ctx.split and ctx.spawn in non-call transfer functions Since OCaml evaluates arguments right-to-left, !r and !spawns are dereferenced before calling the transfer function, which will populate them (uselessly). By using let, evaluation order is enforced. --- src/framework/constraints.ml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index e0e36801ed..7df4167acd 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -713,11 +713,13 @@ struct let tf_assign var edge prev_node lv e getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.assign ctx lv e) !r !spawns + let d = S.assign ctx lv e in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let tf_vdecl var edge prev_node v getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.vdecl ctx v) !r !spawns + let d = S.vdecl ctx v in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let normal_return r fd ctx sideg = let spawning_return = S.return ctx r fd in @@ -732,7 +734,7 @@ struct let tf_ret var edge prev_node ret fd getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = + let d = (* Force transfer function to be evaluated before dereferencing in common_join argument. *) if (CilType.Fundec.equal fd MyCFG.dummy_func || List.mem fd.svar.vname (get_string_list "mainfun")) && get_bool "kernel" @@ -747,11 +749,13 @@ struct let c: unit -> S.C.t = snd var |> Obj.obj in side_context sideg fd (c ()); let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.body ctx fd) !r !spawns + let d = S.body ctx fd in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let tf_test var edge prev_node e tv getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.branch ctx e tv) !r !spawns + let d = S.branch ctx e tv in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let tf_normal_call ctx lv e (f:fundec) args getl sidel getg sideg = let combine (cd, fc, fd) = @@ -870,11 +874,13 @@ struct let tf_asm var edge prev_node getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.asm ctx) !r !spawns + let d = S.asm ctx in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let tf_skip var edge prev_node getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.skip ctx) !r !spawns + let d = S.skip ctx in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let tf var getl sidel getg sideg prev_node edge d = begin match edge with From 7fcb10cd0fcb4c4f68d43a7bb60fec9d7cfc64d5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 18 Jun 2024 17:20:55 +0300 Subject: [PATCH 234/566] Handle pthread_rwlock_t as opaque mutex in base analysis Avoids unsound rwlock struct content invariants in witnesses. --- src/cdomain/value/cdomains/valueDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index de64fde807..09593fb614 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -120,7 +120,7 @@ struct | _ -> false let is_mutex_type (t: typ): bool = match t with - | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" || info.tname = "pthread_cond_t" + | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" || info.tname = "pthread_cond_t" || info.tname = "pthread_rwlock_t" | TInt (IInt, attr) -> hasAttribute "mutex" attr | _ -> false From 62f01a9778ccc61b3894a292a522ee397394ab5f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 18 Jun 2024 18:05:57 +0300 Subject: [PATCH 235/566] Add cram test for int witness invariants --- tests/regression/witness/int.t/int.c | 17 ++++++++++ tests/regression/witness/int.t/run.t | 47 ++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 tests/regression/witness/int.t/int.c create mode 100644 tests/regression/witness/int.t/run.t diff --git a/tests/regression/witness/int.t/int.c b/tests/regression/witness/int.t/int.c new file mode 100644 index 0000000000..4ad15b09ad --- /dev/null +++ b/tests/regression/witness/int.t/int.c @@ -0,0 +1,17 @@ +#include +extern int __VERIFIER_nondet_int(); + +int main() { + int i; + i = __VERIFIER_nondet_int(); + + if (i < 100) + __goblint_check(1); + + if (50 < i && i < 100) + __goblint_check(1); + + if (i == 42 || i == 5 || i == 101) + __goblint_check(1); + return 0; +} diff --git a/tests/regression/witness/int.t/run.t b/tests/regression/witness/int.t/run.t new file mode 100644 index 0000000000..28c670d475 --- /dev/null +++ b/tests/regression/witness/int.t/run.t @@ -0,0 +1,47 @@ + $ goblint --enable ana.sv-comp.functions --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --enable ana.int.def_exc --enable ana.int.enums --enable ana.int.interval --enable ana.int.congruence --enable ana.int.interval_set --disable witness.invariant.split-conjunction int.c + [Success][Assert] Assertion "1" will succeed (int.c:9:5-9:23) + [Success][Assert] Assertion "1" will succeed (int.c:12:5-12:23) + [Success][Assert] Assertion "1" will succeed (int.c:15:5-15:23) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 10 + dead: 0 + total lines: 10 + [Info][Witness] witness generation summary: + total generation entries: 3 + + $ yamlWitnessStrip < witness.yml + - entry_type: location_invariant + location: + file_name: int.c + file_hash: $FILE_HASH + line: 15 + column: 5 + function: main + location_invariant: + string: ((((0 <= i && i <= 127) && i != 0) && (5 <= i && i <= 101)) && ((i == + 5 || i == 42) || i == 101)) && ((i == 5 || i == 42) || i == 101) + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: int.c + file_hash: $FILE_HASH + line: 12 + column: 5 + function: main + location_invariant: + string: (((0 <= i && i != 0) && (51 <= i && i <= 99)) && (0 <= i && i != 0)) && + (51 <= i && i <= 99) + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: int.c + file_hash: $FILE_HASH + line: 9 + column: 5 + function: main + location_invariant: + string: i <= 99 && i <= 99 + type: assertion + format: C From ca5f0646bc18d055d36501b9db5ca72d09854818 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 19 Jun 2024 11:25:32 +0300 Subject: [PATCH 236/566] Extract inclusion list invariant in IntDomain --- src/cdomain/value/cdomains/intDomain.ml | 30 ++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index c525732d3b..89be71ea92 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -582,6 +582,24 @@ module IntervalArith (Ints_t : IntOps.IntOps) = struct List.exists (Z.equal l) ts end +module IntInvariant = +struct + let of_incl_list e ik ps = + match ps with + | [_; _] when ik = IBool && not (get_bool "witness.invariant.inexact-type-bounds") -> + assert (List.mem Z.zero ps); + assert (List.mem Z.one ps); + Invariant.none + | [_] when get_bool "witness.invariant.exact" -> + Invariant.none + | _ :: _ :: _ + | [_] | [] -> + List.fold_left (fun a x -> + let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in + Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) (Invariant.bot ()) ps +end + module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = struct let name () = "intervals" @@ -2731,19 +2749,11 @@ module Enums : S with type int_t = Z.t = struct let ne ik x y = c_lognot ik (eq ik x y) let invariant_ikind e ik x = - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in match x with - | Inc ps when not inexact_type_bounds && ik = IBool && is_top_of ik x -> - Invariant.none | Inc ps -> - if BISet.cardinal ps > 1 || get_bool "witness.invariant.exact" then - BISet.fold (fun x a -> - let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in - Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) ps (Invariant.bot ()) - else - Invariant.top () + IntInvariant.of_incl_list e ik (BISet.elements ps) | Exc (ns, r) -> + let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in (* 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 From e767f90fdcce9eb4348341129915a515d2539d4e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 19 Jun 2024 11:26:43 +0300 Subject: [PATCH 237/566] Simplify inclusion list invariants --- src/cdomain/value/cdomains/intDomain.ml | 12 ++++++---- .../56-witness/46-top-bool-invariant.t | 24 +------------------ tests/regression/witness/int.t/run.t | 3 +-- 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 89be71ea92..b9723bdb5b 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -3797,10 +3797,14 @@ module IntDomTupleImpl = struct else Invariant.top () | None -> - let is = to_list (mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.invariant_ikind e ik } x) - in List.fold_left (fun a i -> - Invariant.(a && i) - ) (Invariant.top ()) is + match to_incl_list x with + | Some ps -> + IntInvariant.of_incl_list e ik ps + | None -> + let is = to_list (mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.invariant_ikind e ik } x) + in List.fold_left (fun a i -> + Invariant.(a && i) + ) (Invariant.top ()) is let arbitrary ik = QCheck.(set_print show @@ tup5 (option (I1.arbitrary ik)) (option (I2.arbitrary ik)) (option (I3.arbitrary ik)) (option (I4.arbitrary ik)) (option (I5.arbitrary ik))) diff --git a/tests/regression/56-witness/46-top-bool-invariant.t b/tests/regression/56-witness/46-top-bool-invariant.t index b04d33dda8..741b00966f 100644 --- a/tests/regression/56-witness/46-top-bool-invariant.t +++ b/tests/regression/56-witness/46-top-bool-invariant.t @@ -144,7 +144,7 @@ all: dead: 0 total lines: 2 [Info][Witness] witness generation summary: - total generation entries: 3 + total generation entries: 1 $ yamlWitnessStrip < witness.yml - entry_type: location_invariant @@ -158,28 +158,6 @@ all: string: x == (_Bool)0 || x == (_Bool)1 type: assertion format: C - - entry_type: location_invariant - location: - file_name: 46-top-bool-invariant.c - file_hash: $FILE_HASH - line: 5 - column: 3 - function: main - location_invariant: - string: x <= (_Bool)1 - type: assertion - format: C - - entry_type: location_invariant - location: - file_name: 46-top-bool-invariant.c - file_hash: $FILE_HASH - line: 5 - column: 3 - function: main - location_invariant: - string: (_Bool)0 <= x - type: assertion - format: C all without inexact-type-bounds: diff --git a/tests/regression/witness/int.t/run.t b/tests/regression/witness/int.t/run.t index 28c670d475..6f75a2cd17 100644 --- a/tests/regression/witness/int.t/run.t +++ b/tests/regression/witness/int.t/run.t @@ -18,8 +18,7 @@ column: 5 function: main location_invariant: - string: ((((0 <= i && i <= 127) && i != 0) && (5 <= i && i <= 101)) && ((i == - 5 || i == 42) || i == 101)) && ((i == 5 || i == 42) || i == 101) + string: (i == 5 || i == 42) || i == 101 type: assertion format: C - entry_type: location_invariant From 176941d1a1582f45eec20ad5d3f9c1d648bffc7d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 19 Jun 2024 11:34:09 +0300 Subject: [PATCH 238/566] Extract definite int invariant in IntDomain --- src/cdomain/value/cdomains/intDomain.ml | 29 ++++++++++--------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index b9723bdb5b..f0715c2e57 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -584,6 +584,12 @@ end module IntInvariant = struct + let of_int e ik x = + if get_bool "witness.invariant.exact" then + Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) + else + Invariant.none + let of_incl_list e ik ps = match ps with | [_; _] when ik = IBool && not (get_bool "witness.invariant.inexact-type-bounds") -> @@ -936,11 +942,7 @@ struct let invariant_ikind e ik x = match x with | Some (x1, x2) when Ints_t.compare x1 x2 = 0 -> - 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 () + IntInvariant.of_int e ik (Ints_t.to_bigint x1) | Some (x1, x2) -> let (min_ik, max_ik) = range ik in let (x1', x2') = BatTuple.Tuple2.mapn (Ints_t.to_bigint) (x1, x2) in @@ -2315,10 +2317,7 @@ struct let invariant_ikind e ik (x:t) = match x with | `Definite x -> - if get_bool "witness.invariant.exact" then - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) - else - Invariant.top () + IntInvariant.of_int e ik x | `Excluded (s, r) -> (* Emit range invariant if tighter than ikind bounds. This can be more precise than interval, which has been widened. *) @@ -3253,10 +3252,7 @@ struct match x with | x when is_top x -> Invariant.top () | Some (c, m) when m =: Z.zero -> - if get_bool "witness.invariant.exact" then - Invariant.of_exp Cil.(BinOp (Eq, e, Cil.kintegerCilint ik c, intType)) - else - Invariant.top () + IntInvariant.of_int e ik c | Some (c, m) -> let open Cil in let (c, m) = BatTuple.Tuple2.mapn (fun a -> kintegerCilint ik a) (c, m) in @@ -3791,11 +3787,8 @@ module IntDomTupleImpl = struct let invariant_ikind e ik x = match to_int x with | Some v -> - 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 () + (* If definite, output single equality instead of every subdomain repeating same equality *) + IntInvariant.of_int e ik v | None -> match to_incl_list x with | Some ps -> From 250196bfb9ee6f2cd2195cc9b46b1c275452a731 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 19 Jun 2024 11:44:39 +0300 Subject: [PATCH 239/566] Extract interval invariant in IntDomain --- src/cdomain/value/cdomains/intDomain.ml | 44 ++++++++++--------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index f0715c2e57..bdd403bbce 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -604,6 +604,17 @@ struct let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) (Invariant.bot ()) ps + + let of_interval e ik (x1, x2) = + if Z.equal x1 x2 then + of_int e ik x1 + else ( + let (min_ik, max_ik) = Size.range ik in + let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in + let i1 = if inexact_type_bounds || Z.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 || Z.compare x2 max_ik <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) else Invariant.none in + Invariant.(i1 && i2) + ) end module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = @@ -939,17 +950,10 @@ struct else if Ints_t.compare y2 x1 <= 0 then of_bool ik false else top_bool - let invariant_ikind e ik x = - match x with - | Some (x1, x2) when Ints_t.compare x1 x2 = 0 -> - IntInvariant.of_int e ik (Ints_t.to_bigint x1) + let invariant_ikind e ik = function | 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 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) + let (x1', x2') = BatTuple.Tuple2.mapn Ints_t.to_bigint (x1, x2) in + IntInvariant.of_interval e ik (x1', x2') | None -> Invariant.none let arbitrary ik = @@ -2322,17 +2326,11 @@ struct (* 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 inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in - let imin = if inexact_type_bounds || Z.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 || Z.compare rmax ikmax <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik rmax, intType)) else Invariant.none in + let ri = IntInvariant.of_interval e ik (rmin, rmax) 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.(imin && imax) + ) s ri | `Bot -> Invariant.none let arbitrary ik = @@ -2752,20 +2750,14 @@ module Enums : S with type int_t = Z.t = struct | Inc ps -> IntInvariant.of_incl_list e ik (BISet.elements ps) | Exc (ns, r) -> - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in (* 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 inexact_type_bounds || Z.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 || Z.compare rmax ikmax <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik rmax, intType)) else Invariant.none in + let ri = IntInvariant.of_interval e ik (rmin, rmax) in BISet.fold (fun x a -> let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in Invariant.(a && i) - ) ns Invariant.(imin && imax) + ) ns ri let arbitrary ik = From 9a25b4859c45671b416281784e26f8e1781ca7b2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 19 Jun 2024 11:49:15 +0300 Subject: [PATCH 240/566] Extract exclusion list invariant in IntDomain --- src/cdomain/value/cdomains/intDomain.ml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index bdd403bbce..79c672f689 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -615,6 +615,12 @@ struct let i2 = if inexact_type_bounds || Z.compare x2 max_ik <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) else Invariant.none in Invariant.(i1 && i2) ) + + let of_excl_list e ik ns = + List.fold_left (fun a x -> + let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in + Invariant.(a && i) + ) (Invariant.top ()) ns end module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = @@ -2327,10 +2333,8 @@ struct 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 ri = IntInvariant.of_interval e ik (rmin, rmax) in - S.fold (fun x a -> - let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in - Invariant.(a && i) - ) s ri + let si = IntInvariant.of_excl_list e ik (S.elements s) in + Invariant.(ri && si) | `Bot -> Invariant.none let arbitrary ik = @@ -2754,10 +2758,8 @@ module Enums : S with type int_t = Z.t = struct 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 ri = IntInvariant.of_interval e ik (rmin, rmax) in - BISet.fold (fun x a -> - let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in - Invariant.(a && i) - ) ns ri + let nsi = IntInvariant.of_excl_list e ik (BISet.elements ns) in + Invariant.(ri && nsi) let arbitrary ik = From d1f04bc23150d0b186d52f1ccaf170f54916bd8f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 19 Jun 2024 12:35:20 +0300 Subject: [PATCH 241/566] Simplify interval and exclusion list invariants --- src/cdomain/value/cdomains/intDomain.ml | 39 ++++++++++++++++++------- tests/regression/cfg/foo.t/run.t | 24 +-------------- tests/regression/witness/int.t/run.t | 3 +- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 79c672f689..8860a8fbb5 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -605,16 +605,28 @@ struct Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) (Invariant.bot ()) ps - let of_interval e ik (x1, x2) = - if Z.equal x1 x2 then + let of_interval_opt e ik = function + | (Some x1, Some x2) when Z.equal x1 x2 -> of_int e ik x1 - else ( + | x1_opt, x2_opt -> let (min_ik, max_ik) = Size.range ik in let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in - let i1 = if inexact_type_bounds || Z.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 || Z.compare x2 max_ik <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) else Invariant.none in + let i1 = + match x1_opt, inexact_type_bounds with + | Some x1, false when Z.equal min_ik x1 -> Invariant.none + | Some x1, _ -> Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1, e, intType)) + | None, _ -> Invariant.none + in + let i2 = + match x2_opt, inexact_type_bounds with + | Some x2, false when Z.equal x2 max_ik -> Invariant.none + | Some x2, _ -> Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) + | None, _ -> Invariant.none + in Invariant.(i1 && i2) - ) + + let of_interval e ik (x1, x2) = + of_interval_opt e ik (Some x1, Some x2) let of_excl_list e ik ns = List.fold_left (fun a x -> @@ -3778,7 +3790,7 @@ module IntDomTupleImpl = struct | Some v when not (GobConfig.get_bool "dbg.full-output") -> BatPrintf.fprintf f "\n\n%s\n\n\n" (Z.to_string v) | _ -> BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) - let invariant_ikind e ik x = + let invariant_ikind e ik ((_, _, _, x_cong, x_intset) as x) = match to_int x with | Some v -> (* If definite, output single equality instead of every subdomain repeating same equality *) @@ -3788,10 +3800,15 @@ module IntDomTupleImpl = struct | Some ps -> IntInvariant.of_incl_list e ik ps | None -> - let is = to_list (mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.invariant_ikind e ik } x) - in List.fold_left (fun a i -> - Invariant.(a && i) - ) (Invariant.top ()) is + let min = minimal x in + let max = maximal x in + let ns = Option.map fst (to_excl_list x) |? [] in + Invariant.( + IntInvariant.of_interval_opt e ik (min, max) && + IntInvariant.of_excl_list e ik ns && + Option.map_default (I4.invariant_ikind e ik) Invariant.none x_cong && + Option.map_default (I5.invariant_ikind e ik) Invariant.none x_intset + ) let arbitrary ik = QCheck.(set_print show @@ tup5 (option (I1.arbitrary ik)) (option (I2.arbitrary ik)) (option (I3.arbitrary ik)) (option (I4.arbitrary ik)) (option (I5.arbitrary ik))) diff --git a/tests/regression/cfg/foo.t/run.t b/tests/regression/cfg/foo.t/run.t index 02f6c1e5e0..705fb8d497 100644 --- a/tests/regression/cfg/foo.t/run.t +++ b/tests/regression/cfg/foo.t/run.t @@ -67,7 +67,7 @@ total lines: 6 [Warning][Deadcode][CWE-571] condition 'a > 0' (possibly inserted by CIL) is always true (foo.c:3:10-3:20) [Info][Witness] witness generation summary: - total generation entries: 15 + total generation entries: 13 $ yamlWitnessStrip < witness.yml - entry_type: loop_invariant @@ -125,17 +125,6 @@ string: 1 <= a type: assertion format: C - - entry_type: location_invariant - location: - file_name: foo.c - file_hash: $FILE_HASH - line: 7 - column: 3 - function: main - location_invariant: - string: 0 <= a - type: assertion - format: C - entry_type: location_invariant location: file_name: foo.c @@ -224,14 +213,3 @@ string: 1 <= a type: assertion format: C - - entry_type: location_invariant - location: - file_name: foo.c - file_hash: $FILE_HASH - line: 4 - column: 5 - function: main - location_invariant: - string: 0 <= a - type: assertion - format: C diff --git a/tests/regression/witness/int.t/run.t b/tests/regression/witness/int.t/run.t index 6f75a2cd17..33316791e7 100644 --- a/tests/regression/witness/int.t/run.t +++ b/tests/regression/witness/int.t/run.t @@ -29,8 +29,7 @@ column: 5 function: main location_invariant: - string: (((0 <= i && i != 0) && (51 <= i && i <= 99)) && (0 <= i && i != 0)) && - (51 <= i && i <= 99) + string: (51 <= i && i <= 99) && ((i != 0 && i != 0) && (51 <= i && i <= 99)) type: assertion format: C - entry_type: location_invariant From 39f6c2d8d0d1c4f059263bba840fec4cb5aead34 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 19 Jun 2024 12:41:09 +0300 Subject: [PATCH 242/566] Deduplicate inclusion list elements in IntDomTuple --- src/cdomain/value/cdomains/intDomain.ml | 2 +- tests/regression/witness/int.t/run.t | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 8860a8fbb5..c2eb2f3aa5 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -3536,7 +3536,7 @@ module IntDomTupleImpl = struct let merge ps = let (vs, rs) = List.split ps in let (mins, maxs) = List.split rs in - (List.concat vs, (List.min mins, List.max maxs)) + (List.concat vs |> List.sort_uniq Z.compare, (List.min mins, List.max maxs)) in mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.to_excl_list } x |> flat merge diff --git a/tests/regression/witness/int.t/run.t b/tests/regression/witness/int.t/run.t index 33316791e7..2bf5bd31c3 100644 --- a/tests/regression/witness/int.t/run.t +++ b/tests/regression/witness/int.t/run.t @@ -29,7 +29,7 @@ column: 5 function: main location_invariant: - string: (51 <= i && i <= 99) && ((i != 0 && i != 0) && (51 <= i && i <= 99)) + string: (51 <= i && i <= 99) && (i != 0 && (51 <= i && i <= 99)) type: assertion format: C - entry_type: location_invariant From 2408ab6ae413a4bfcefa149c5a76d086382e3f86 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 19 Jun 2024 12:44:40 +0300 Subject: [PATCH 243/566] Filter out-of-invariant exclusion list elements in IntDomTuple invariant --- src/cdomain/value/cdomains/intDomain.ml | 2 ++ tests/regression/cfg/foo.t/run.t | 35 +------------------------ tests/regression/witness/int.t/run.t | 2 +- 3 files changed, 4 insertions(+), 35 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index c2eb2f3aa5..a472a5017e 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -3803,6 +3803,8 @@ module IntDomTupleImpl = struct let min = minimal x in let max = maximal x in let ns = Option.map fst (to_excl_list x) |? [] in + let ns = Option.map_default (fun min -> List.filter (Z.leq min) ns) ns min in + let ns = Option.map_default (fun max -> List.filter (Z.geq max) ns) ns max in Invariant.( IntInvariant.of_interval_opt e ik (min, max) && IntInvariant.of_excl_list e ik ns && diff --git a/tests/regression/cfg/foo.t/run.t b/tests/regression/cfg/foo.t/run.t index 705fb8d497..cd890b7a19 100644 --- a/tests/regression/cfg/foo.t/run.t +++ b/tests/regression/cfg/foo.t/run.t @@ -67,7 +67,7 @@ total lines: 6 [Warning][Deadcode][CWE-571] condition 'a > 0' (possibly inserted by CIL) is always true (foo.c:3:10-3:20) [Info][Witness] witness generation summary: - total generation entries: 13 + total generation entries: 10 $ yamlWitnessStrip < witness.yml - entry_type: loop_invariant @@ -103,17 +103,6 @@ string: b == 0 type: assertion format: C - - entry_type: location_invariant - location: - file_name: foo.c - file_hash: $FILE_HASH - line: 7 - column: 3 - function: main - location_invariant: - string: a != 0 - type: assertion - format: C - entry_type: location_invariant location: file_name: foo.c @@ -147,17 +136,6 @@ string: b != 0 type: assertion format: C - - entry_type: location_invariant - location: - file_name: foo.c - file_hash: $FILE_HASH - line: 5 - column: 5 - function: main - location_invariant: - string: a != 1 - type: assertion - format: C - entry_type: location_invariant location: file_name: foo.c @@ -191,17 +169,6 @@ string: b != 0 type: assertion format: C - - entry_type: location_invariant - location: - file_name: foo.c - file_hash: $FILE_HASH - line: 4 - column: 5 - function: main - location_invariant: - string: a != 0 - type: assertion - format: C - entry_type: location_invariant location: file_name: foo.c diff --git a/tests/regression/witness/int.t/run.t b/tests/regression/witness/int.t/run.t index 2bf5bd31c3..bc8ad5ac0a 100644 --- a/tests/regression/witness/int.t/run.t +++ b/tests/regression/witness/int.t/run.t @@ -29,7 +29,7 @@ column: 5 function: main location_invariant: - string: (51 <= i && i <= 99) && (i != 0 && (51 <= i && i <= 99)) + string: (51 <= i && i <= 99) && (51 <= i && i <= 99) type: assertion format: C - entry_type: location_invariant From 0a27c74d21a66a34abd5ecb6bbfe052d064fa261 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 19 Jun 2024 12:57:06 +0300 Subject: [PATCH 244/566] Document nicer IntDomTuple invariant --- src/cdomain/value/cdomains/intDomain.ml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index a472a5017e..797c0f46c1 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -3793,23 +3793,26 @@ module IntDomTupleImpl = struct let invariant_ikind e ik ((_, _, _, x_cong, x_intset) as x) = match to_int x with | Some v -> - (* If definite, output single equality instead of every subdomain repeating same equality *) + (* If definite, output single equality instead of every subdomain repeating same equality (or something less precise). *) IntInvariant.of_int e ik v | None -> match to_incl_list x with | Some ps -> + (* If inclusion set, output disjunction of equalities because it subsumes interval(s), exclusion set and congruence. *) IntInvariant.of_incl_list e ik ps | None -> + (* Get interval bounds from all domains (intervals and exclusion set ranges). *) let min = minimal x in let max = maximal x in - let ns = Option.map fst (to_excl_list x) |? [] in + let ns = Option.map fst (to_excl_list x) |? [] in (* Ignore exclusion set bit range, known via interval bounds already. *) + (* "Refine" out-of-bounds exclusions for simpler output. *) let ns = Option.map_default (fun min -> List.filter (Z.leq min) ns) ns min in let ns = Option.map_default (fun max -> List.filter (Z.geq max) ns) ns max in Invariant.( - IntInvariant.of_interval_opt e ik (min, max) && + IntInvariant.of_interval_opt e ik (min, max) && (* Output best interval bounds once instead of multiple subdomains repeating them (or less precise ones). *) IntInvariant.of_excl_list e ik ns && - Option.map_default (I4.invariant_ikind e ik) Invariant.none x_cong && - Option.map_default (I5.invariant_ikind e ik) Invariant.none x_intset + Option.map_default (I4.invariant_ikind e ik) Invariant.none x_cong && (* Output congruence as is. *) + Option.map_default (I5.invariant_ikind e ik) Invariant.none x_intset (* Output interval sets as is. *) ) let arbitrary ik = QCheck.(set_print show @@ tup5 (option (I1.arbitrary ik)) (option (I2.arbitrary ik)) (option (I3.arbitrary ik)) (option (I4.arbitrary ik)) (option (I5.arbitrary ik))) From 02ef2f96d96e15bf94231dfdc5ddebe7b6d3606b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 19 Jun 2024 12:59:08 +0300 Subject: [PATCH 245/566] Disable interval set in witness int invariant cram test --- tests/regression/witness/int.t/run.t | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/witness/int.t/run.t b/tests/regression/witness/int.t/run.t index bc8ad5ac0a..6b4784ce32 100644 --- a/tests/regression/witness/int.t/run.t +++ b/tests/regression/witness/int.t/run.t @@ -1,4 +1,4 @@ - $ goblint --enable ana.sv-comp.functions --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --enable ana.int.def_exc --enable ana.int.enums --enable ana.int.interval --enable ana.int.congruence --enable ana.int.interval_set --disable witness.invariant.split-conjunction int.c + $ goblint --enable ana.sv-comp.functions --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --enable ana.int.def_exc --enable ana.int.enums --enable ana.int.interval --enable ana.int.congruence --disable ana.int.interval_set --disable witness.invariant.split-conjunction int.c [Success][Assert] Assertion "1" will succeed (int.c:9:5-9:23) [Success][Assert] Assertion "1" will succeed (int.c:12:5-12:23) [Success][Assert] Assertion "1" will succeed (int.c:15:5-15:23) @@ -29,7 +29,7 @@ column: 5 function: main location_invariant: - string: (51 <= i && i <= 99) && (51 <= i && i <= 99) + string: 51 <= i && i <= 99 type: assertion format: C - entry_type: location_invariant @@ -40,6 +40,6 @@ column: 5 function: main location_invariant: - string: i <= 99 && i <= 99 + string: i <= 99 type: assertion format: C From c9ea4408e20c8968912c7773677b696c6f050222 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 19 Jun 2024 15:49:17 +0300 Subject: [PATCH 246/566] Binops on offsets should not generate overflow warnings in SV-COMP --- src/cdomain/value/cdomains/offset.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomain/value/cdomains/offset.ml b/src/cdomain/value/cdomains/offset.ml index fa64f71daa..e8cba0afc5 100644 --- a/src/cdomain/value/cdomains/offset.ml +++ b/src/cdomain/value/cdomains/offset.ml @@ -213,7 +213,7 @@ struct let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in let bits_offset = idx_of_int bits_offset in let remaining_offset = offset_to_index_offset ~typ:field.ftype o in - Idx.add bits_offset remaining_offset + GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> Idx.add bits_offset remaining_offset | `Index (x, o) -> let (item_typ, item_size_in_bits) = match Option.map unrollType typ with From 545206f68a9dee4999860ec4ce6232f214d65516 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 19 Jun 2024 16:07:19 +0300 Subject: [PATCH 247/566] Add some info about overflows on pointer arithmetics to assumptions --- docs/user-guide/assumptions.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/user-guide/assumptions.md b/docs/user-guide/assumptions.md index f77e3b5097..16386987de 100644 --- a/docs/user-guide/assumptions.md +++ b/docs/user-guide/assumptions.md @@ -17,3 +17,12 @@ _NB! This list is likely incomplete._ See [PR #1414](https://github.com/goblint/analyzer/pull/1414). +2. Goblint's does not give any guarantees about overflows not happening during pointer arithmetics. + + Although the analysis can detect and warn about overflows that might happen during operations with pointer offsets, + the analysis does not warn about overflows from operations with the possible addresses (as integers) of the pointers themselves. + + This affects the `no-overflows` analysis from `ana.int.interval` analysis. + + See further discussion from [PR #1511](https://github.com/goblint/analyzer/pull/1511). + From af781ed684c538cfa98ef0d35a9d258fec62e495 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 20 Jun 2024 12:56:40 +0300 Subject: [PATCH 248/566] Enable ana.float.evaluate_math_functions in svcomp24 and svcomp confs This is needed for sv-benchmarks Juliet no-overflow tasks involving sqrt. We used this at SV-COMP 2024, before the option existed. --- conf/svcomp.json | 3 ++- conf/svcomp24-validate.json | 3 ++- conf/svcomp24.json | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index 467d294bdd..d2bea96040 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -10,7 +10,8 @@ "interval": true }, "float": { - "interval": true + "interval": true, + "evaluate_math_functions": true }, "activated": [ "base", diff --git a/conf/svcomp24-validate.json b/conf/svcomp24-validate.json index 7832ffa6af..d83b1767a4 100644 --- a/conf/svcomp24-validate.json +++ b/conf/svcomp24-validate.json @@ -10,7 +10,8 @@ "interval": true }, "float": { - "interval": true + "interval": true, + "evaluate_math_functions": true }, "activated": [ "base", diff --git a/conf/svcomp24.json b/conf/svcomp24.json index 7e30554ceb..1c60f84920 100644 --- a/conf/svcomp24.json +++ b/conf/svcomp24.json @@ -10,7 +10,8 @@ "interval": true }, "float": { - "interval": true + "interval": true, + "evaluate_math_functions": true }, "activated": [ "base", From 3ff00aea295c6e7386323efc88f38d1a046cbdbc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 20 Jun 2024 13:00:36 +0300 Subject: [PATCH 249/566] Add tests for imaxabs --- .../39-signed-overflows/11-imaxabs.c | 24 +++++++++++++++++++ .../39-signed-overflows/12-imaxabs-sqrt.c | 12 ++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/regression/39-signed-overflows/11-imaxabs.c create mode 100644 tests/regression/39-signed-overflows/12-imaxabs-sqrt.c diff --git a/tests/regression/39-signed-overflows/11-imaxabs.c b/tests/regression/39-signed-overflows/11-imaxabs.c new file mode 100644 index 0000000000..dce200a146 --- /dev/null +++ b/tests/regression/39-signed-overflows/11-imaxabs.c @@ -0,0 +1,24 @@ +//PARAM: --enable ana.int.interval --set ana.activated[+] tmpSpecial +#include +#include +#include +int main() { + int64_t data; + if (data > (-0x7fffffffffffffff - 1)) + { + if (imaxabs(data) < 100) + { + __goblint_check(data < 100); // TODO + __goblint_check(-100 < data); // TODO + int64_t result = data * data; // TODO NOWARN + } + + if(imaxabs(data) <= 100) + { + __goblint_check(data <= 100); // TODO + __goblint_check(-100 <= data); // TODO + int64_t result = data * data; // TODO NOWARN + } + } + return 8; +} diff --git a/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c b/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c new file mode 100644 index 0000000000..b121645b27 --- /dev/null +++ b/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c @@ -0,0 +1,12 @@ +//PARAM: --enable ana.int.interval --enable ana.float.interval --enable ana.float.evaluate_math_functions --set ana.activated[+] tmpSpecial +#include +#include +#include +int main() { + int64_t data; + if (data > (-0x7fffffffffffffff - 1) && imaxabs((intmax_t)data) <= sqrtl(0x7fffffffffffffffLL)) + { + int64_t result = data * data; // TODO NOWARN + } + return 8; +} From 2653e2ea22dd9d012a10e008f3189e1061d2c344 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 20 Jun 2024 13:02:26 +0300 Subject: [PATCH 250/566] Add hacky imaxabs support --- src/util/library/libraryFunctions.ml | 2 +- tests/regression/39-signed-overflows/11-imaxabs.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index e7ff2a4d04..df90339c65 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -139,7 +139,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("abs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (IInt, j)) }); ("labs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILong, j)) }); ("llabs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILongLong, j)) }); - ("imaxabs", unknown [drop "j" []]); + ("imaxabs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILong, j)) }); (* TODO: look up intmax_t ikind from CIL file *) ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) diff --git a/tests/regression/39-signed-overflows/11-imaxabs.c b/tests/regression/39-signed-overflows/11-imaxabs.c index dce200a146..47bd26569f 100644 --- a/tests/regression/39-signed-overflows/11-imaxabs.c +++ b/tests/regression/39-signed-overflows/11-imaxabs.c @@ -8,16 +8,16 @@ int main() { { if (imaxabs(data) < 100) { - __goblint_check(data < 100); // TODO - __goblint_check(-100 < data); // TODO - int64_t result = data * data; // TODO NOWARN + __goblint_check(data < 100); + __goblint_check(-100 < data); + int64_t result = data * data; // NOWARN } if(imaxabs(data) <= 100) { - __goblint_check(data <= 100); // TODO - __goblint_check(-100 <= data); // TODO - int64_t result = data * data; // TODO NOWARN + __goblint_check(data <= 100); + __goblint_check(-100 <= data); + int64_t result = data * data; // NOWARN } } return 8; From f9765da81d64a99f77c385835c6c0a5c3db419da Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 20 Jun 2024 13:05:26 +0300 Subject: [PATCH 251/566] Add hacky imaxabs sqrt refine support --- src/analyses/baseInvariant.ml | 3 ++- tests/regression/39-signed-overflows/12-imaxabs-sqrt.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 51a27e19f8..d5b65a95f4 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -785,7 +785,8 @@ struct | TFloat (fk, _), FLongDouble | TFloat (FDouble as fk, _), FDouble | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st - | _ -> fallback (fun () -> Pretty.text "CastE: incompatible types") st) + | TInt (ik, _), _ -> inv_exp (Int (FD.to_int ik c)) e st (* TODO: is this cast refinement correct? *) + | t, fk -> fallback (fun () -> Pretty.dprintf "CastE: incompatible types %a and %a" CilType.Typ.pretty t CilType.Fkind.pretty fk) 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 diff --git a/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c b/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c index b121645b27..46512aed21 100644 --- a/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c +++ b/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c @@ -6,7 +6,7 @@ int main() { int64_t data; if (data > (-0x7fffffffffffffff - 1) && imaxabs((intmax_t)data) <= sqrtl(0x7fffffffffffffffLL)) { - int64_t result = data * data; // TODO NOWARN + int64_t result = data * data; // NOWARN } return 8; } From a1f0b35703e34da0eda8f3f27ea260a58fd2c85d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 20 Jun 2024 13:15:01 +0300 Subject: [PATCH 252/566] Find intmax_t for imaxabs from program --- src/util/library/libraryFunctions.ml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index df90339c65..689eb17126 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -6,6 +6,16 @@ open GobConfig module M = Messages +let intmax_t = lazy ( + let res = ref None in + GoblintCil.iterGlobals !Cilfacade.current_file (function + | GType ({tname = "intmax_t"; ttype; _}, _) -> + res := Some ttype; + | _ -> () + ); + !res +) + (** C standard library functions. These are specified by the C standard. *) let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ @@ -139,7 +149,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("abs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (IInt, j)) }); ("labs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILong, j)) }); ("llabs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILongLong, j)) }); - ("imaxabs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (ILong, j)) }); (* TODO: look up intmax_t ikind from CIL file *) + ("imaxabs", special [__ "j" []] @@ fun j -> Math { fun_args = (Abs (Cilfacade.get_ikind (Option.get (Lazy.force intmax_t)), j)) }); ("localtime_r", unknown [drop "timep" [r]; drop "result" [w]]); ("strpbrk", unknown [drop "s" [r]; drop "accept" [r]]); ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) From 06086c646285037bcb242dc70705fbaa690e8ebd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 20 Jun 2024 17:55:07 +0300 Subject: [PATCH 253/566] Refactor YAML witness validation result passing --- src/framework/control.ml | 14 +++++++++----- src/witness/witness.ml | 25 ++++++++----------------- src/witness/yamlWitness.ml | 16 +++++++++++++++- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index 05fad90caf..bb1d7bab58 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -789,15 +789,19 @@ struct ); (* Before SV-COMP, so result can depend on YAML witness validation. *) - if get_string "witness.yaml.validate" <> "" then ( - let module YWitness = YamlWitness.Validator (R) in - YWitness.validate () - ); + let yaml_validate_result = + if get_string "witness.yaml.validate" <> "" then ( + let module YWitness = YamlWitness.Validator (R) in + Some (YWitness.validate ()) + ) + else + None + in if get_bool "ana.sv-comp.enabled" then ( (* SV-COMP and witness generation *) let module WResult = Witness.Result (R) in - WResult.write entrystates + WResult.write yaml_validate_result entrystates ); if get_bool "witness.yaml.enabled" then ( diff --git a/src/witness/witness.ml b/src/witness/witness.ml index fb88c2ce7b..651e7a76d5 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -690,25 +690,16 @@ struct Timing.wrap "graphml witness" (write_file witness_path (module Task)) (module TaskResult) ) - let write entrystates = + let write yaml_validate_result entrystates = match !AnalysisState.verified with | Some false -> print_svcomp_result "ERROR (verify)" | _ -> - if get_string "witness.yaml.validate" <> "" then ( - match get_bool "witness.yaml.strict" with - | true when !YamlWitness.cnt_error > 0 -> - print_svcomp_result "ERROR (witness error)" - | true when !YamlWitness.cnt_unsupported > 0 -> - print_svcomp_result "ERROR (witness unsupported)" - | true when !YamlWitness.cnt_disabled > 0 -> - print_svcomp_result "ERROR (witness disabled)" - | _ when !YamlWitness.cnt_refuted > 0 -> - print_svcomp_result (Result.to_string (False None)) - | _ when !YamlWitness.cnt_unconfirmed > 0 -> - print_svcomp_result (Result.to_string Unknown) - | _ -> - write entrystates - ) - else + match yaml_validate_result with + | Some (Error msg) -> + print_svcomp_result ("ERROR (" ^ msg ^ ")") + | Some (Ok (Svcomp.Result.False _ | Unknown as result)) -> + print_svcomp_result (Result.to_string result) + | Some (Ok True) + | None -> write entrystates end diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 42254f30de..71bb75afef 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -840,5 +840,19 @@ struct let certificate_path = GobConfig.get_string "witness.yaml.certificate" in if certificate_path <> "" then - yaml_entries_to_file (List.rev yaml_entries') (Fpath.v certificate_path) + yaml_entries_to_file (List.rev yaml_entries') (Fpath.v certificate_path); + + match GobConfig.get_bool "witness.yaml.strict" with + | true when !cnt_error > 0 -> + Error "witness error" + | true when !cnt_unsupported > 0 -> + Error "witness unsupported" + | true when !cnt_disabled > 0 -> + Error "witness disabled" + | _ when !cnt_refuted > 0 -> + Ok (Svcomp.Result.False None) + | _ when !cnt_unconfirmed > 0 -> + Ok Unknown + | _ -> + Ok True end From a74840942fabc3231eb41eb62ce073a35b667971 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 21 Jun 2024 13:20:34 +0300 Subject: [PATCH 254/566] Upgrade locked opam dependencies --- goblint.opam.locked | 91 ++++++++++++++++++++++++--------------------- gobview | 2 +- 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/goblint.opam.locked b/goblint.opam.locked index d532457ced..410d363ac8 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -21,89 +21,94 @@ homepage: "https://goblint.in.tum.de" doc: "https://goblint.readthedocs.io/en/latest/" bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ - "angstrom" {= "0.15.0"} - "apron" {= "v0.9.14~beta.2"} + "angstrom" {= "0.16.0"} + "apron" {= "v0.9.14"} "arg-complete" {= "0.1.0"} "astring" {= "0.8.5"} "base-bigarray" {= "base"} "base-bytes" {= "base"} "base-threads" {= "base"} "base-unix" {= "base"} - "batteries" {= "3.6.0"} + "batteries" {= "3.8.0"} "benchmark" {= "1.6" & with-test} "bigarray-compat" {= "1.1.0"} - "bigstringaf" {= "0.9.0"} + "bigstringaf" {= "0.9.1"} "bos" {= "0.2.1"} - "camlidl" {= "1.11"} + "camlidl" {= "1.12"} "camlp-streams" {= "5.0.1"} "catapult" {= "0.2"} "catapult-file" {= "0.2"} - "cmdliner" {= "1.1.1" & with-doc} - "conf-autoconf" {= "0.1"} + "cmdliner" {= "1.3.0" & with-doc} + "conf-autoconf" {= "0.2"} + "conf-findutils" {= "1"} "conf-gcc" {= "1.0"} "conf-gmp" {= "4"} - "conf-mpfr" {= "3"} + "conf-gmp-paths" {= "1"} + "conf-mpfr-paths" {= "1"} "conf-perl" {= "2"} - "conf-pkg-config" {= "2"} "conf-ruby" {= "1.0.0" & with-test} - "conf-which" {= "1"} "cppo" {= "1.6.9"} "cpu" {= "2.0.0"} - "csexp" {= "1.5.1"} - "ctypes" {= "0.20.1"} - "dune" {= "3.7.1"} - "dune-build-info" {= "3.7.1"} - "dune-configurator" {= "3.7.1"} - "dune-private-libs" {= "3.7.1"} - "dune-site" {= "3.7.1"} - "dyn" {= "3.7.1"} + "crunch" {= "3.3.1" & with-doc} + "csexp" {= "1.5.2"} + "cstruct" {= "6.2.0"} + "ctypes" {= "0.22.0"} + "dune" {= "3.16.0"} + "dune-build-info" {= "3.16.0"} + "dune-configurator" {= "3.16.0"} + "dune-private-libs" {= "3.16.0"} + "dune-site" {= "3.16.0"} + "dyn" {= "3.16.0"} + "ez-conf-lib" {= "2"} "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} "goblint-cil" {= "2.0.3"} + "hex" {= "1.5.0"} "integers" {= "0.7.0"} - "json-data-encoding" {= "0.12.1"} - "jsonrpc" {= "1.15.0~5.0preview1"} + "json-data-encoding" {= "1.0.1"} + "jsonrpc" {= "1.17.0"} "logs" {= "0.7.0"} - "mlgmpidl" {= "1.2.15"} - "num" {= "1.4"} + "mlgmpidl" {= "1.3.0"} + "num" {= "1.5"} "ocaml" {= "4.14.0"} "ocaml-compiler-libs" {= "v0.12.4"} "ocaml-config" {= "2"} "ocaml-option-flambda" {= "1"} "ocaml-syntax-shims" {= "1.0.0"} "ocaml-variants" {= "4.14.0+options"} - "ocamlbuild" {= "0.14.2"} - "ocamlfind" {= "1.9.5"} - "odoc" {= "2.2.0" & with-doc} - "odoc-parser" {= "2.0.0" & with-doc} - "ordering" {= "3.7.1"} - "ounit2" {= "2.2.6" & with-test} - "pp" {= "1.1.2"} + "ocamlbuild" {= "0.14.3"} + "ocamlfind" {= "1.9.6"} + "odoc" {= "2.4.2" & with-doc} + "odoc-parser" {= "2.4.2" & with-doc} + "ordering" {= "3.16.0"} + "ounit2" {= "2.2.7" & with-test} + "pp" {= "1.2.0"} "ppx_derivers" {= "1.2.1"} "ppx_deriving" {= "6.0.2"} "ppx_deriving_hash" {= "0.1.2"} - "ppx_deriving_yojson" {= "3.7.0"} + "ppx_deriving_yojson" {= "3.8.0"} "ppxlib" {= "0.32.1"} - "qcheck-core" {= "0.20"} - "qcheck-ounit" {= "0.20" & with-test} - "re" {= "1.10.4" & with-doc} - "result" {= "1.5"} + "ptime" {= "1.1.0" & with-doc} + "qcheck-core" {= "0.21.3"} + "qcheck-ounit" {= "0.21.3" & with-test} + "re" {= "1.11.0" & with-doc} + "result" {= "1.5" & with-doc} "rresult" {= "0.7.0"} "seq" {= "base"} - "sexplib0" {= "v0.15.1"} - "sha" {= "1.15.2"} + "sexplib0" {= "v0.16.0"} + "sha" {= "1.15.4"} "stdlib-shims" {= "0.3.0"} - "stdune" {= "3.7.1"} + "stdune" {= "3.16.0"} "stringext" {= "1.6.0"} - "topkg" {= "1.0.6"} - "tyxml" {= "4.5.0" & with-doc} - "uri" {= "4.2.0"} + "topkg" {= "1.0.7"} + "tyxml" {= "4.6.0" & with-doc} + "uri" {= "4.4.0"} "uuidm" {= "0.9.8"} "uutf" {= "1.0.3" & with-doc} - "yaml" {= "3.1.0"} - "yojson" {= "2.0.2"} - "zarith" {= "1.12"} + "yaml" {= "3.2.0"} + "yojson" {= "2.2.1"} + "zarith" {= "1.13"} ] build: [ ["dune" "subst"] {dev} diff --git a/gobview b/gobview index 195c61cbae..287bf59be9 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 195c61cbae86b8ffb8af4a21f2acd689665c1c0e +Subproject commit 287bf59be9e25ceea704384c65e0d91565bb749d From 702af102571ec98d660b5ec898d62a73dd4d616c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 21 Jun 2024 14:50:45 +0300 Subject: [PATCH 255/566] Use Sys.opaque_identity in build-info to prevent excessive no-changes rebuild in release profile --- src/build-info/dune | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/build-info/dune b/src/build-info/dune index 5a64b399a4..e1a45ef8fc 100644 --- a/src/build-info/dune +++ b/src/build-info/dune @@ -15,7 +15,7 @@ (target configVersion.ml) (mode (promote (until-clean) (only configVersion.ml))) ; replace existing file in source tree, even if releasing (only overrides) (deps (universe)) ; do not cache, always regenerate - (action (pipe-stdout (bash "git describe --all --long --dirty || echo \"n/a\"") (with-stdout-to %{target} (bash "xargs printf '(* Automatically regenerated, changes do not persist! *)\nlet version = \"%s\"'"))))) + (action (pipe-stdout (bash "git describe --all --long --dirty || echo \"n/a\"") (with-stdout-to %{target} (bash "xargs printf '(* Automatically regenerated, changes do not persist! *)\nlet version = Sys.opaque_identity \"%s\"'"))))) (rule (target configProfile.ml) @@ -31,7 +31,7 @@ (target configDatetime.ml) (mode (promote (until-clean) (only configDatetime.ml))) ; replace existing file in source tree, even if releasing (only overrides) (deps (universe)) ; do not cache, always regenerate - (action (pipe-stdout (bash "date +\"%Y-%m-%dT%H:%M:%S\" || echo \"n/a\"") (with-stdout-to %{target} (bash "xargs printf '(* Automatically regenerated, changes do not persist! *)\nlet datetime = \"%s\"'"))))) + (action (pipe-stdout (bash "date +\"%Y-%m-%dT%H:%M:%S\" || echo \"n/a\"") (with-stdout-to %{target} (bash "xargs printf '(* Automatically regenerated, changes do not persist! *)\nlet datetime = Sys.opaque_identity \"%s\"'"))))) (env (_ From 01d275829fa8c532cbf44ab3a439f04bd3886e19 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 21 Jun 2024 15:19:14 +0300 Subject: [PATCH 256/566] Use String.starts_with and ends_with from Stdlib Since OCaml 4.13. --- src/analyses/mutexEventsAnalysis.ml | 5 ++--- src/autoTune.ml | 2 +- src/build-info/dune | 1 - src/build-info/goblint_build_info.ml | 2 +- src/cdomain/value/domains/invariantCil.ml | 4 ++-- src/cdomains/apron/affineEqualityDomain.apron.ml | 5 ++--- src/common/util/cilfacade.ml | 2 +- src/domains/access.ml | 9 ++++----- src/incremental/compareAST.ml | 2 +- src/incremental/makefileUtil.ml | 2 +- src/witness/svcomp.ml | 5 ++--- 11 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index c5339e68cf..5ea0afc809 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -5,7 +5,6 @@ module M = Messages module Addr = ValueDomain.Addr module LF = LibraryFunctions -open Batteries open GoblintCil open Analyses open GobConfig @@ -52,12 +51,12 @@ struct let return ctx exp fundec : D.t = (* deprecated but still valid SV-COMP convention for atomic block *) - if get_bool "ana.sv-comp.functions" && String.starts_with fundec.svar.vname "__VERIFIER_atomic_" then + if get_bool "ana.sv-comp.functions" && String.starts_with fundec.svar.vname ~prefix:"__VERIFIER_atomic_" then ctx.emit (Events.Unlock (LockDomain.Addr.of_var LF.verifier_atomic_var)) let body ctx f : D.t = (* deprecated but still valid SV-COMP convention for atomic block *) - if get_bool "ana.sv-comp.functions" && String.starts_with f.svar.vname "__VERIFIER_atomic_" then + if get_bool "ana.sv-comp.functions" && String.starts_with f.svar.vname ~prefix:"__VERIFIER_atomic_" then ctx.emit (Events.Lock (LockDomain.Addr.of_var LF.verifier_atomic_var, true)) let special (ctx: (unit, _, _, _) ctx) lv f arglist : D.t = diff --git a/src/autoTune.ml b/src/autoTune.ml index 434b4fb0b2..3871e421c7 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -104,7 +104,7 @@ let rec setCongruenceRecursive fd depth neigbourFunction = | exception Not_found -> () (* Happens for __goblint_bounded *) ) (FunctionSet.filter (*for extern and builtin functions there is no function definition in CIL*) - (fun x -> not (isExtern x.vstorage || BatString.starts_with x.vname "__builtin")) + (fun x -> not (isExtern x.vstorage || String.starts_with x.vname ~prefix:"__builtin")) (neigbourFunction fd.svar) ) ; diff --git a/src/build-info/dune b/src/build-info/dune index e1a45ef8fc..1dd74c192c 100644 --- a/src/build-info/dune +++ b/src/build-info/dune @@ -8,7 +8,6 @@ (library (name goblint_build_info) (public_name goblint.build-info) - (libraries batteries.unthreaded) (virtual_modules dune_build_info)) (rule diff --git a/src/build-info/goblint_build_info.ml b/src/build-info/goblint_build_info.ml index 14e30a1b36..d2600c94ad 100644 --- a/src/build-info/goblint_build_info.ml +++ b/src/build-info/goblint_build_info.ml @@ -18,7 +18,7 @@ let release_commit = "%%VCS_COMMIT_ID%%" (** Goblint version. *) let version = let commit = ConfigVersion.version in - if BatString.starts_with release_version "%" then + if String.starts_with release_version ~prefix:"%" then commit else ( let commit = diff --git a/src/cdomain/value/domains/invariantCil.ml b/src/cdomain/value/domains/invariantCil.ml index 813ec25818..f41d48ab61 100644 --- a/src/cdomain/value/domains/invariantCil.ml +++ b/src/cdomain/value/domains/invariantCil.ml @@ -88,7 +88,7 @@ class exp_contains_anon_type_visitor = object inherit nopCilVisitor method! vtype (t: typ) = match t with - | TComp ({cname; _}, _) when BatString.starts_with_stdlib ~prefix:"__anon" cname -> + | TComp ({cname; _}, _) when String.starts_with ~prefix:"__anon" cname -> raise Stdlib.Exit | _ -> DoChildren @@ -102,7 +102,7 @@ let exp_contains_anon_type = (* TODO: synchronize magic constant with BaseDomain *) -let var_is_heap {vname; _} = BatString.starts_with vname "(alloc@" +let var_is_heap {vname; _} = String.starts_with vname ~prefix:"(alloc@" let reset_lazy () = ResettableLazy.reset exclude_vars_regexp diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 7e60cce74b..ab9c1994fb 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -6,7 +6,6 @@ Matrices are modeled as proposed by Karr: Each variable is assigned to a column and each row represents a linear affine relationship that must hold at the corresponding program point. The apron environment is hereby used to organize the order of columns and variables. *) -open Batteries open GoblintCil open Pretty module M = Messages @@ -193,7 +192,7 @@ struct in let res = (String.concat "" @@ Array.to_list @@ Array.map dim_to_str vars) ^ (const_to_str arr.(Array.length arr - 1)) ^ "=0" in - if String.starts_with res "+" then + if String.starts_with res ~prefix:"+" then Str.string_after res 1 else res @@ -370,7 +369,7 @@ struct let remove_rels_with_var x var env inplace = timing_wrap "remove_rels_with_var" (remove_rels_with_var x var env) inplace let forget_vars t vars = - if is_bot t || is_top_env t || List.is_empty vars then + if is_bot t || is_top_env t || vars = [] then t else let m = Option.get t.d in diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 99430ee8b6..62ce993455 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -734,7 +734,7 @@ let add_function_declarations (file: Cil.file): unit = let functions, non_functions = List.partition (fun g -> match g with GFun _ -> true | _ -> false) globals in let upto_last_type, non_types = GobList.until_last_with (fun g -> match g with GType _ -> true | _ -> false) non_functions in let declaration_from_GFun f = match f with - | GFun (f, _) when BatString.starts_with_stdlib ~prefix:"__builtin" f.svar.vname -> + | GFun (f, _) when String.starts_with ~prefix:"__builtin" f.svar.vname -> (* Builtin functions should not occur in asserts generated, so there is no need to add declarations for them.*) None | GFun (f, _) -> diff --git a/src/domains/access.ml b/src/domains/access.ml index c35fb3a16d..f7ce68a18b 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -1,6 +1,5 @@ (** Memory accesses and their manipulation. *) -open Batteries open GoblintCil open Pretty open GobConfig @@ -12,7 +11,7 @@ module M = Messages let is_ignorable_comp_name = function | "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE" -> true - | cname when String.starts_with_stdlib ~prefix:"__anon" cname -> + | cname when String.starts_with ~prefix:"__anon" cname -> begin match Cilfacade.split_anoncomp_name cname with | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) | (false, Some ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) @@ -385,7 +384,7 @@ and distribute_access_exp f = function and distribute_access_type f = function | TArray (et, len, _) -> - Option.may (distribute_access_exp f) len; + Option.iter (distribute_access_exp f) len; distribute_access_type f et | TVoid _ @@ -434,7 +433,7 @@ struct include SetDomain.Make (A) let max_conf accs = - accs |> elements |> List.map (fun {A.conf; _} -> conf) |> (List.max ~cmp:Int.compare) + accs |> elements |> List.map (fun {A.conf; _} -> conf) |> (BatList.max ~cmp:Int.compare) end @@ -583,7 +582,7 @@ let incr_summary ~safe ~vulnerable ~unsafe grouped_accs = |> List.filter_map race_conf |> (function | [] -> None - | confs -> Some (List.max confs) + | confs -> Some (BatList.max confs) ) in match safety with diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index f3de153658..ecd69f1f96 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -73,7 +73,7 @@ let forward_list_equal ?(propF = (&&>>)) f l1 l2 ~(rename_mapping: rename_mappin let compare_name (a: string) (b: string) = let anon_struct = "__anonstruct_" in let anon_union = "__anonunion_" in - if a = b then true else BatString.(starts_with a anon_struct && starts_with b anon_struct || starts_with a anon_union && starts_with b anon_union) + if a = b then true else String.(starts_with a ~prefix:anon_struct && starts_with b ~prefix:anon_struct || starts_with a ~prefix:anon_union && starts_with b ~prefix:anon_union) let rec eq_constant ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) (a: constant) (b: constant) : bool * rename_mapping = match a, b with diff --git a/src/incremental/makefileUtil.ml b/src/incremental/makefileUtil.ml index 9a17122f5e..33cd8f64c9 100644 --- a/src/incremental/makefileUtil.ml +++ b/src/incremental/makefileUtil.ml @@ -39,7 +39,7 @@ let find_file_by_suffix (dir: Fpath.t) (file_name_suffix: string) = | (h::t) -> let f = Fpath.to_string h in if Sys.file_exists f && Sys.is_directory f then (Queue.add h dirs; search dir t) - else if Batteries.String.ends_with (Fpath.filename h) file_name_suffix then h else search dir t + else if String.ends_with (Fpath.filename h) ~suffix:file_name_suffix then h else search dir t | [] -> if Queue.is_empty dirs then failwith ("find_file_by_suffix found no files with suffix "^file_name_suffix^" in "^ Fpath.to_string dir) else let d = Queue.take dirs in search d (list_files d) diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index bb887e6cb1..c17b5e78f8 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -1,7 +1,6 @@ (** SV-COMP tasks and results. *) open GoblintCil -open Batteries module Specification = SvcompSpec @@ -27,9 +26,9 @@ let is_error_function f = (* TODO: unused, but should be used? *) let is_special_function f = let loc = f.vdecl in - let is_svcomp = String.ends_with loc.file "sv-comp.c" in (* only includes/sv-comp.c functions, not __VERIFIER_assert in benchmark *) + let is_svcomp = String.ends_with loc.file ~suffix:"sv-comp.c" in (* only includes/sv-comp.c functions, not __VERIFIER_assert in benchmark *) let is_verifier = match f.vname with - | fname when String.starts_with fname "__VERIFIER" -> true + | fname when String.starts_with fname ~prefix:"__VERIFIER" -> true | fname -> is_error_function f in is_svcomp && is_verifier From ecda3f734f020703b0535e578513abfd9b2d53a4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 25 Jun 2024 11:24:45 +0300 Subject: [PATCH 257/566] Move comment in MutexTypeAnalysis --- src/analyses/mutexTypeAnalysis.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index e8edddd41e..4a993bbd7d 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -12,17 +12,17 @@ struct let name () = "pthreadMutexType" - (* Removing indexes here avoids complicated lookups and allows to have the LVals as vars here, at the price that different types of mutexes in arrays are not dinstinguished *) - module O = Offset.Unit - module V = struct + (* Removing indexes here avoids complicated lookups and allows to have the LVals as vars here, at the price that different types of mutexes in arrays are not dinstinguished *) include Mval.Unit let is_write_only _ = false end module G = MAttr + module O = Offset.Unit + (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = match lval with From bc3fac895299e7b96ad8ac216d446bc6751a831c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 25 Jun 2024 11:51:07 +0300 Subject: [PATCH 258/566] Add witness missing verdict to YAML witness validation Better than "EXCEPTION (Failure)" as verdict. --- src/goblint.ml | 5 +++++ src/witness/svcomp.ml | 5 +++++ src/witness/witness.ml | 2 +- src/witness/yamlWitness.ml | 4 +++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/goblint.ml b/src/goblint.ml index a687badb8e..52b9bbdfc0 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -83,6 +83,11 @@ let main () = Logs.error "%s" (MessageUtil.colorize ~fd:Unix.stderr ("{RED}Analysis was aborted because it reached the set timeout of " ^ get_string "dbg.timeout" ^ " or was signalled SIGPROF!")); Goblint_timing.teardown_tef (); exit 124 + | Svcomp.Error msg -> + do_stats (); + Witness.print_svcomp_result ("ERROR (" ^ msg ^ ")"); + Goblint_timing.teardown_tef (); + exit 1 (* We do this since the evaluation order of top-level bindings is not defined, but we want `main` to run after all the other side-effects (e.g. registering analyses/solvers) have happened. *) let () = at_exit main diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index bb887e6cb1..59a8f01b40 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -60,6 +60,11 @@ struct | Unknown -> "unknown" end +exception Error of string + +let errorwith s = raise (Error s) + + module type TaskResult = sig module Arg: MyARG.S diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 651e7a76d5..7b0213b601 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -695,7 +695,7 @@ struct | Some false -> print_svcomp_result "ERROR (verify)" | _ -> match yaml_validate_result with - | Some (Error msg) -> + | Some (Stdlib.Error msg) -> print_svcomp_result ("ERROR (" ^ msg ^ ")") | Some (Ok (Svcomp.Result.False _ | Unknown as result)) -> print_svcomp_result (Result.to_string result) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 71bb75afef..bb91e195ef 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -581,7 +581,9 @@ struct let yaml = match Yaml_unix.of_file (Fpath.v (GobConfig.get_string "witness.yaml.validate")) with | Ok yaml -> yaml - | Error (`Msg m) -> failwith ("Yaml_unix.of_file: " ^ m) + | Error (`Msg m) -> + Logs.error "Yaml_unix.of_file: %s" m; + Svcomp.errorwith "witness missing" in let yaml_entries = yaml |> GobYaml.list |> BatResult.get_ok in From 9fd0fd5bd3dcb7b22d200e4f655535789d846d51 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 25 Jun 2024 11:59:00 +0300 Subject: [PATCH 259/566] Add witness missing verdict to YAML witness unassume Better than "EXCEPTION (Failure)" as verdict. --- 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 265e9c6925..85b33edc79 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -85,7 +85,9 @@ struct let yaml = match Yaml_unix.of_file (Fpath.v (GobConfig.get_string "witness.yaml.unassume")) with | Ok yaml -> yaml - | Error (`Msg m) -> failwith ("Yaml_unix.of_file: " ^ m) + | Error (`Msg m) -> + Logs.error "Yaml_unix.of_file: %s" m; + Svcomp.errorwith "witness missing" in let yaml_entries = yaml |> GobYaml.list |> BatResult.get_ok in From 85ab1fdc9edddf65a8f1a6c8bae74c1a417859f0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 25 Jun 2024 12:09:46 +0300 Subject: [PATCH 260/566] Check YAML witness existence before analysis --- src/framework/control.ml | 1 + src/witness/yamlWitness.ml | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/framework/control.ml b/src/framework/control.ml index bb1d7bab58..5e92282210 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -357,6 +357,7 @@ struct (* real beginning of the [analyze] function *) if get_bool "ana.sv-comp.enabled" then Witness.init (module FileCfg); (* TODO: move this out of analyze_loop *) + YamlWitness.init (); AnalysisState.global_initialization := true; GobConfig.earlyglobs := get_bool "exp.earlyglobs"; diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index bb91e195ef..7134211d32 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -514,6 +514,15 @@ struct Timing.wrap "yaml witness" write () end +let init () = + match GobConfig.get_string "witness.yaml.validate" with + | "" -> () + | path -> + (* Check witness existence before doing the analysis. *) + if not (Sys.file_exists path) then ( + Logs.error "witness.yaml.validate: %s not found" path; + Svcomp.errorwith "witness missing" + ) module ValidationResult = struct From 05397c58b551d94dfb3532ed9952ab0d50edfc51 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 25 Jun 2024 15:22:44 +0300 Subject: [PATCH 261/566] Add Karoliine to authors --- .zenodo.json | 5 +++++ CITATION.cff | 4 ++++ dune-project | 2 +- goblint.opam | 1 + goblint.opam.locked | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.zenodo.json b/.zenodo.json index 22705c2d9c..1e71573948 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -23,6 +23,11 @@ "affiliation": "Technische Universität München", "orcid": "0009-0009-9644-7475" }, + { + "name": "Holter, Karoliine", + "affiliation": "University of Tartu", + "orcid": "0009-0008-3725-4131" + }, { "name": "Vogler, Ralf", "affiliation": "Technische Universität München" diff --git a/CITATION.cff b/CITATION.cff index 25d46cf762..7a93859c54 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -21,6 +21,10 @@ authors: # same authors as in .zenodo.json and dune-project family-names: Tilscher affiliation: "Technische Universität München" orcid: "https://orcid.org/0009-0009-9644-7475" + - given-names: Karoliine + family-names: Holter + affiliation: "University of Tartu" + orcid: "https://orcid.org/0009-0008-3725-4131" - given-names: Ralf family-names: Vogler affiliation: "Technische Universität München" diff --git a/dune-project b/dune-project index 878abd3b4f..b85a82b93f 100644 --- a/dune-project +++ b/dune-project @@ -15,7 +15,7 @@ (source (github goblint/analyzer)) (homepage "https://goblint.in.tum.de") (documentation "https://goblint.readthedocs.io/en/latest/") -(authors "Simmo Saan" "Michael Schwarz" "Julian Erhard" "Sarah Tilscher" "Ralf Vogler" "Kalmer Apinis" "Vesal Vojdani" ) ; same authors as in .zenodo.json and CITATION.cff +(authors "Simmo Saan" "Michael Schwarz" "Julian Erhard" "Sarah Tilscher" "Karoliine Holter" "Ralf Vogler" "Kalmer Apinis" "Vesal Vojdani" ) ; same authors as in .zenodo.json and CITATION.cff (maintainers "Simmo Saan " "Michael Schwarz " "Karoliine Holter") (license MIT) diff --git a/goblint.opam b/goblint.opam index 692625c965..03e11d456a 100644 --- a/goblint.opam +++ b/goblint.opam @@ -11,6 +11,7 @@ authors: [ "Michael Schwarz" "Julian Erhard" "Sarah Tilscher" + "Karoliine Holter" "Ralf Vogler" "Kalmer Apinis" "Vesal Vojdani" diff --git a/goblint.opam.locked b/goblint.opam.locked index f8de683948..0cf9a2ff75 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -12,6 +12,7 @@ authors: [ "Michael Schwarz" "Julian Erhard" "Sarah Tilscher" + "Karoliine Holter" "Ralf Vogler" "Kalmer Apinis" "Vesal Vojdani" From 1e705fa4925863dd9a52a7b9e5070c9f7b97fe0a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 25 Jun 2024 21:56:56 +0300 Subject: [PATCH 262/566] Rewrite pointer arithmethic not overflowing assumption --- docs/user-guide/assumptions.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/user-guide/assumptions.md b/docs/user-guide/assumptions.md index 16386987de..33284ccc00 100644 --- a/docs/user-guide/assumptions.md +++ b/docs/user-guide/assumptions.md @@ -17,12 +17,22 @@ _NB! This list is likely incomplete._ See [PR #1414](https://github.com/goblint/analyzer/pull/1414). -2. Goblint's does not give any guarantees about overflows not happening during pointer arithmetics. +2. Pointer arithmetic does not overflow. - Although the analysis can detect and warn about overflows that might happen during operations with pointer offsets, - the analysis does not warn about overflows from operations with the possible addresses (as integers) of the pointers themselves. + [C11's N1570][n1570] at 6.5.6.8 states that - This affects the `no-overflows` analysis from `ana.int.interval` analysis. + > When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. + > [...] + > the evaluation shall not produce an overflow; otherwise, the behavior is undefined. - See further discussion from [PR #1511](https://github.com/goblint/analyzer/pull/1511). + after a long list of defined behaviors. + Goblint does not report overflow and out-of-bounds pointer arithmetic (when the pointer _is not dereferenced_). + This affects the overflow analysis (SV-COMP no-overflow property) in the `base` analysis. + + This _does not_ affect the `memOutOfBounds` analysis (SV-COMP valid-memsafety property), which is for undefined behavior from _dereferencing_ such out-of-bounds pointers. + + See [PR #1511](https://github.com/goblint/analyzer/pull/1511). + + +[n1570]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf From a1bfc416569028f2f03c43d5df9840e2ac9aa3e6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 26 Jun 2024 11:45:25 +0300 Subject: [PATCH 263/566] Upgrade locked OCaml to 4.14.2 --- .github/workflows/coverage.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/locked.yml | 6 +++--- .github/workflows/unlocked.yml | 8 ++++---- goblint.opam.locked | 4 ++-- gobview | 2 +- make.sh | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index d4fc872620..1e1991e2ab 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -18,7 +18,7 @@ jobs: os: - ubuntu-latest ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used runs-on: ${{ matrix.os }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 242acef3de..bae49437dd 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -18,7 +18,7 @@ jobs: os: - ubuntu-latest ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used runs-on: ${{ matrix.os }} diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 18935725ca..a7f8787305 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -20,7 +20,7 @@ jobs: - ubuntu-latest - macos-13 ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used runs-on: ${{ matrix.os }} @@ -73,7 +73,7 @@ jobs: os: - ubuntu-latest ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used runs-on: ${{ matrix.os }} @@ -116,7 +116,7 @@ jobs: os: - ubuntu-latest ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used node-version: - 14 diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index db41ad3007..af32008400 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -21,7 +21,7 @@ jobs: - 5.2.x - 5.1.x - 5.0.x - - ocaml-variants.4.14.0+options,ocaml-option-flambda + - ocaml-variants.4.14.2+options,ocaml-option-flambda - 4.14.x apron: - false @@ -92,7 +92,7 @@ jobs: - ubuntu-latest - macos-13 ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file, downgrade deps step + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file, downgrade deps step name: lower-bounds (${{ matrix.os }}, ${{ matrix.ocaml-compiler }}, downgrade) @@ -133,7 +133,7 @@ jobs: - name: Downgrade dependencies # must specify ocaml-base-compiler again to prevent it from being downgraded # prevent num downgrade to avoid dune/jbuilder error: https://github.com/ocaml/dune/issues/5280 - run: opam install $(opam exec -- opam-0install --prefer-oldest goblint ocaml-variants.4.14.0+options ocaml-option-flambda num.1.5) + run: opam install $(opam exec -- opam-0install --prefer-oldest goblint ocaml-variants.4.14.2+options ocaml-option-flambda num.1.5) - name: Build run: ./make.sh nat @@ -190,7 +190,7 @@ jobs: - ubuntu-latest - macos-13 ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file runs-on: ${{ matrix.os }} diff --git a/goblint.opam.locked b/goblint.opam.locked index 410d363ac8..26f8bb53e7 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -71,12 +71,12 @@ depends: [ "logs" {= "0.7.0"} "mlgmpidl" {= "1.3.0"} "num" {= "1.5"} - "ocaml" {= "4.14.0"} + "ocaml" {= "4.14.2"} "ocaml-compiler-libs" {= "v0.12.4"} "ocaml-config" {= "2"} "ocaml-option-flambda" {= "1"} "ocaml-syntax-shims" {= "1.0.0"} - "ocaml-variants" {= "4.14.0+options"} + "ocaml-variants" {= "4.14.2+options"} "ocamlbuild" {= "0.14.3"} "ocamlfind" {= "1.9.6"} "odoc" {= "2.4.2" & with-doc} diff --git a/gobview b/gobview index 287bf59be9..4069f32f82 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 287bf59be9e25ceea704384c65e0d91565bb749d +Subproject commit 4069f32f82efdefb43c970fb77ca29671a4b6972 diff --git a/make.sh b/make.sh index 5b55e51beb..0f76759065 100755 --- a/make.sh +++ b/make.sh @@ -8,7 +8,7 @@ opam_setup() { set -x opam init -y -a --bare $SANDBOXING # sandboxing is disabled in travis and docker opam update - opam switch -y create . --deps-only --packages=ocaml-variants.4.14.0+options,ocaml-option-flambda --locked + opam switch -y create . --deps-only --packages=ocaml-variants.4.14.2+options,ocaml-option-flambda --locked } rule() { From 56ed7b5589da639d5ec9028c8558cb75810dada1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 26 Jun 2024 17:05:20 +0300 Subject: [PATCH 264/566] Add option ana.base.invariant.int.simplify --- src/cdomain/value/cdomains/intDomain.ml | 26 ++++++++++++++++++++----- src/config/options.schema.json | 14 +++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 797c0f46c1..2d40e6a161 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -3791,11 +3791,15 @@ module IntDomTupleImpl = struct | _ -> BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) let invariant_ikind e ik ((_, _, _, x_cong, x_intset) as x) = - match to_int x with - | Some v -> - (* If definite, output single equality instead of every subdomain repeating same equality (or something less precise). *) - IntInvariant.of_int e ik v - | None -> + let simplify_int fallback = + match to_int x with + | Some v -> + (* If definite, output single equality instead of every subdomain repeating same equality (or something less precise). *) + IntInvariant.of_int e ik v + | None -> + fallback () + in + let simplify_all () = match to_incl_list x with | Some ps -> (* If inclusion set, output disjunction of equalities because it subsumes interval(s), exclusion set and congruence. *) @@ -3814,6 +3818,18 @@ module IntDomTupleImpl = struct Option.map_default (I4.invariant_ikind e ik) Invariant.none x_cong && (* Output congruence as is. *) Option.map_default (I5.invariant_ikind e ik) Invariant.none x_intset (* Output interval sets as is. *) ) + in + let simplify_none () = + let is = to_list (mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.invariant_ikind e ik } x) in + List.fold_left (fun a i -> + Invariant.(a && i) + ) (Invariant.top ()) is + in + match GobConfig.get_string "ana.base.invariant.int.simplify" with + | "none" -> simplify_none () + | "int" -> simplify_int simplify_none + | "all" -> simplify_int simplify_all + | _ -> assert false let arbitrary ik = QCheck.(set_print show @@ tup5 (option (I1.arbitrary ik)) (option (I2.arbitrary ik)) (option (I3.arbitrary ik)) (option (I4.arbitrary ik)) (option (I5.arbitrary ik))) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index acf85abed9..27cc0c7d93 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -807,6 +807,20 @@ "type": "string", "enum": ["once", "fixpoint"], "default": "once" + }, + "int": { + "title": "ana.base.invariant.int", + "type": "object", + "properties": { + "simplify": { + "title": "ana.base.invariant.int.simplify", + "description": "How much to simplify int domain invariants. Value \"int\" only simplifies definite integers. Without int domain refinement \"all\" might not be maximally precise.", + "type": "string", + "enum": ["none", "int", "all"], + "default": "all" + } + }, + "additionalProperties": false } }, "additionalProperties": false From 14e5523ce2b49eed48ec72837d9a41eff4b0f3e3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 27 Jun 2024 11:40:07 +0300 Subject: [PATCH 265/566] Remove small margin for Apron domain show --- src/cdomains/apron/apronDomain.apron.ml | 2 +- src/common/util/gobFormat.ml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index d0ef268ca6..26e954f1b2 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -442,7 +442,7 @@ struct let invariant _ = [] let show (x:t) = - Format.asprintf "%a (env: %a)" A.print x (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x) + Format.asprintf "%t%a (env: %a)" GobFormat.pp_set_infinite_geometry A.print x (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x) let pretty () (x:t) = text (show x) let equal x y = diff --git a/src/common/util/gobFormat.ml b/src/common/util/gobFormat.ml index 3cda0a4758..1b4d28a26c 100644 --- a/src/common/util/gobFormat.ml +++ b/src/common/util/gobFormat.ml @@ -19,3 +19,7 @@ let pp_set_ansi_color_tags ppf = Format.pp_set_mark_tags ppf true let pp_print_nothing (ppf: Format.formatter) () = () + +let pp_infinity = 1000000001 (* Exact value not exposed before OCaml 5.2, but use the smallest value permitted by documentation. *) + +let pp_set_infinite_geometry = Format.pp_set_geometry ~max_indent:(pp_infinity - 2) ~margin:(pp_infinity - 1) From d2527b7f757e486cadfd551c712796cc6ee4c1b2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 27 Jun 2024 11:49:10 +0300 Subject: [PATCH 266/566] Add Printable.SimpleFormat --- src/common/domains/printable.ml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index 1a642c932a..b6b1dc5c3a 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -88,6 +88,20 @@ struct let to_yojson x = `String (show x) end +module type Formatable = +sig + type t + val pp: Format.formatter -> t -> unit +end + +module SimpleFormat (P: Formatable) = +struct + let show x = Format.asprintf "%t%a" GobFormat.pp_set_infinite_geometry P.pp x + let pretty () x = text (show x) + let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape (show x)) + let to_yojson x = `String (show x) +end + module type Name = sig val name: string end module UnitConf (N: Name) = From 6269d1e2942b8567601a30d35657beadeaabecaa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 27 Jun 2024 12:02:59 +0300 Subject: [PATCH 267/566] Add some Printables to GobApron --- .../apron/affineEqualityDomain.apron.ml | 3 +- src/cdomains/apron/apronDomain.apron.ml | 18 +++--- src/cdomains/apron/gobApron.apron.ml | 56 ++++++++++++++++++- .../apron/linearTwoVarEqualityDomain.apron.ml | 6 +- src/cdomains/apron/sharedFunctions.apron.ml | 2 +- 5 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 7e60cce74b..6bef6c329e 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -206,8 +206,7 @@ struct Format.asprintf "%s" ("[|"^ (String.concat "; " constraint_list) ^"|]") let pretty () (x:t) = text (show x) - let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) - + let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) Environment.printXml x.env let eval_interval ask = Bounds.bound_texpr let name () = "affeq" diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 26e954f1b2..826eec0715 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -283,7 +283,7 @@ struct let assign_exp_with ask nd v e no_ov = match Convert.texpr1_of_cil_exp ask nd (A.env nd) e no_ov with | texpr1 -> - if M.tracing then M.trace "apron" "assign_exp converted: %s" (Format.asprintf "%a" Texpr1.print texpr1); + if M.tracing then M.trace "apron" "assign_exp converted: %a" Texpr1.pretty texpr1; A.assign_texpr_with Man.mgr nd v texpr1 None | exception Convert.Unsupported_CilExp _ -> if M.tracing then M.trace "apron" "assign_exp unsupported"; @@ -442,7 +442,7 @@ struct let invariant _ = [] let show (x:t) = - Format.asprintf "%t%a (env: %a)" GobFormat.pp_set_infinite_geometry A.print x (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x) + Format.asprintf "%t%a (env: %a)" GobFormat.pp_set_infinite_geometry A.print x Environment.pp (A.env x) let pretty () (x:t) = text (show x) let equal x y = @@ -454,7 +454,7 @@ struct let compare (x:t) y: int = (* there is no A.compare, but polymorphic compare should delegate to Abstract0 and Environment compare's implemented in Apron's C *) Stdlib.compare x y - let printXml f x = BatPrintf.fprintf f "\n\n\nconstraints\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%a" A.print x)) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x))) + let printXml f x = BatPrintf.fprintf f "\n\n\nconstraints\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (Format.asprintf "%a" A.print x)) Environment.printXml (A.env x) let to_yojson (x: t) = let constraints = @@ -463,11 +463,9 @@ struct |> Lincons1Set.elements |> List.map (fun lincons1 -> `String (Lincons1.show lincons1)) in - let env = `String (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x)) - in `Assoc [ ("constraints", `List constraints); - ("env", env); + ("env", Environment.to_yojson (A.env x)); ] let unify x y = @@ -533,9 +531,9 @@ struct | _ -> begin match Convert.tcons1_of_cil_exp ask d (A.env d) e negate no_ov with | tcons1 -> - if M.tracing then M.trace "apron" "assert_constraint %a %s" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); + if M.tracing then M.trace "apron" "assert_constraint %a %a" d_exp e Tcons1.pretty tcons1; if M.tracing then M.trace "apron" "assert_constraint st: %a" D.pretty d; - if M.tracing then M.trace "apron" "assert_constraint tcons1: %s" (Format.asprintf "%a" Tcons1.print tcons1); + if M.tracing then M.trace "apron" "assert_constraint tcons1: %a" Tcons1.pretty tcons1; let r = meet_tcons ask d tcons1 e in if M.tracing then M.trace "apron" "assert_constraint r: %a" D.pretty r; r @@ -598,7 +596,7 @@ struct let x_cons = A.to_lincons_array Man.mgr x_j in let y_cons = A.to_lincons_array Man.mgr y_j in let try_add_con j con1 = - if M.tracing then M.tracei "apron" "try_add_con %s" (Format.asprintf "%a" (Lincons1.print: Format.formatter -> Lincons1.t -> unit) con1); + if M.tracing then M.tracei "apron" "try_add_con %a" Lincons1.pretty con1; let t = meet_lincons j con1 in let t_x = A.change_environment Man.mgr t x_env false in let t_y = A.change_environment Man.mgr t y_env false in @@ -637,7 +635,7 @@ struct in let env_exists_mem_con1 env con1 = let r = env_exists_mem_con1 env con1 in - if M.tracing then M.trace "apron" "env_exists_mem_con1 %s %s -> %B" (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) env) (Lincons1.show con1) r; + if M.tracing then M.trace "apron" "env_exists_mem_con1 %a %a -> %B" Environment.pretty env Lincons1.pretty con1 r; r in (* Heuristically reorder constraints to pass 36/12 with singlethreaded->multithreaded mode switching. *) diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml index e202a88c60..f2322c1473 100644 --- a/src/cdomains/apron/gobApron.apron.ml +++ b/src/cdomains/apron/gobApron.apron.ml @@ -18,7 +18,14 @@ module Lincons1 = struct include Lincons1 - let show = Format.asprintf "%a" print + let pp = print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) + let compare x y = String.compare (show x) (show y) (* HACK *) let num_vars x = @@ -43,12 +50,59 @@ struct |> of_enum end +module Texpr1 = +struct + include Texpr1 + + let pp = print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) + + module Expr = + struct + type t = expr + + let pp = print_expr + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) + end +end + +module Tcons1 = +struct + include Tcons1 + + let pp = print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) +end + (** A few code elements for environment changes from functions as remove_vars etc. have been moved to sharedFunctions as they are needed in a similar way inside affineEqualityDomain. A module that includes various methods used by variable handling operations such as add_vars, remove_vars etc. in apronDomain and affineEqualityDomain. *) module Environment = struct include Environment + let pp: Format.formatter -> Environment.t -> unit = Environment.print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) + let ivars_only env = let ivs, fvs = Environment.vars env in assert (Array.length fvs = 0); (* shouldn't ever contain floats *) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 5558bc2c96..fa71e3d1a5 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -11,7 +11,7 @@ open Batteries open GoblintCil open Pretty module M = Messages -open Apron +open GobApron open VectorMatrix module Mpqf = SharedFunctions.Mpqf @@ -331,7 +331,7 @@ struct let simplified_monomials_from_texp (t: t) texp = let res = simplified_monomials_from_texp t texp in - if M.tracing then M.tracel "from_texp" "%s %s -> %s" (EConj.show @@ snd @@ BatOption.get t.d) (Format.asprintf "%a" Texpr1.print_expr texp) + if M.tracing then M.tracel "from_texp" "%s %a -> %s" (EConj.show @@ snd @@ BatOption.get t.d) Texpr1.Expr.pretty texp (BatOption.map_default (fun (l,(o,d)) -> List.fold_right (fun (a,x,b) acc -> Printf.sprintf "%s*var_%d/%s + %s" (Z.to_string a) x (Z.to_string b) acc) l ((Z.to_string o)^"/"^(Z.to_string d))) "" res); res @@ -424,7 +424,7 @@ struct EConj.show_formatted (show_var varM.env) (snd arr) ^ (to_subscript @@ fst arr) let pretty () (x:t) = text (show x) - let printXml f x = BatPrintf.fprintf f "\n\n\nequalities\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) + let printXml f x = BatPrintf.fprintf f "\n\n\nequalities\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) Environment.printXml x.env let eval_interval ask = Bounds.bound_texpr let meet_with_one_conj t i (var, o, divi) = diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 8c94c996b0..9627b4762a 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -196,7 +196,7 @@ struct in let exp = Cil.constFold false exp in let res = conv exp in - if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp: %a -> %s (%b)" d_plainexp exp (Format.asprintf "%a" Texpr1.print_expr res) (Lazy.force no_ov); + if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp: %a -> %a (%b)" d_plainexp exp Texpr1.Expr.pretty res (Lazy.force no_ov); res let texpr1_of_cil_exp ask d env e no_ov = From e37b7d756ee7f0fcd4a8f56ded7f01adf89d090f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 27 Jun 2024 12:06:04 +0300 Subject: [PATCH 268/566] Remove pointless `Format.asprintf "%s"` --- src/cdomains/apron/affineEqualityDomain.apron.ml | 4 ++-- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 6bef6c329e..8bb99f2264 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -203,10 +203,10 @@ struct | Some m when Matrix.is_empty m -> "⊤" | Some m -> let constraint_list = List.init (Matrix.num_rows m) (fun i -> vec_to_constraint (conv_to_ints @@ Matrix.get_row m i) t.env) in - Format.asprintf "%s" ("[|"^ (String.concat "; " constraint_list) ^"|]") + "[|"^ (String.concat "; " constraint_list) ^"|]" let pretty () (x:t) = text (show x) - let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) Environment.printXml x.env + let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (show x)) Environment.printXml x.env let eval_interval ask = Bounds.bound_texpr let name () = "affeq" diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index fa71e3d1a5..bd6b81402a 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -424,7 +424,7 @@ struct EConj.show_formatted (show_var varM.env) (snd arr) ^ (to_subscript @@ fst arr) let pretty () (x:t) = text (show x) - let printXml f x = BatPrintf.fprintf f "\n\n\nequalities\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) Environment.printXml x.env + let printXml f x = BatPrintf.fprintf f "\n\n\nequalities\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (show x)) Environment.printXml x.env let eval_interval ask = Bounds.bound_texpr let meet_with_one_conj t i (var, o, divi) = From 5ea925eab57750f77acad4081dc6d46d6016814d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 27 Jun 2024 12:28:09 +0300 Subject: [PATCH 269/566] Extract GobFormat.asprintf --- src/cdomains/apron/apronDomain.apron.ml | 4 ++-- src/common/domains/printable.ml | 2 +- src/common/util/gobFormat.ml | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 826eec0715..777db5a297 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -442,7 +442,7 @@ struct let invariant _ = [] let show (x:t) = - Format.asprintf "%t%a (env: %a)" GobFormat.pp_set_infinite_geometry A.print x Environment.pp (A.env x) + GobFormat.asprintf "%a (env: %a)" A.print x Environment.pp (A.env x) let pretty () (x:t) = text (show x) let equal x y = @@ -454,7 +454,7 @@ struct let compare (x:t) y: int = (* there is no A.compare, but polymorphic compare should delegate to Abstract0 and Environment compare's implemented in Apron's C *) Stdlib.compare x y - let printXml f x = BatPrintf.fprintf f "\n\n\nconstraints\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (Format.asprintf "%a" A.print x)) Environment.printXml (A.env x) + let printXml f x = BatPrintf.fprintf f "\n\n\nconstraints\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (GobFormat.asprint A.print x)) Environment.printXml (A.env x) let to_yojson (x: t) = let constraints = diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index b6b1dc5c3a..ee0eda0766 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -96,7 +96,7 @@ end module SimpleFormat (P: Formatable) = struct - let show x = Format.asprintf "%t%a" GobFormat.pp_set_infinite_geometry P.pp x + let show x = GobFormat.asprint P.pp x let pretty () x = text (show x) let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape (show x)) let to_yojson x = `String (show x) diff --git a/src/common/util/gobFormat.ml b/src/common/util/gobFormat.ml index 1b4d28a26c..8f26ff0087 100644 --- a/src/common/util/gobFormat.ml +++ b/src/common/util/gobFormat.ml @@ -23,3 +23,8 @@ let pp_print_nothing (ppf: Format.formatter) () = () let pp_infinity = 1000000001 (* Exact value not exposed before OCaml 5.2, but use the smallest value permitted by documentation. *) let pp_set_infinite_geometry = Format.pp_set_geometry ~max_indent:(pp_infinity - 2) ~margin:(pp_infinity - 1) + +let asprintf (fmt: ('a, Format.formatter, unit, string) format4): 'a = + Format.asprintf ("%t" ^^ fmt) pp_set_infinite_geometry + +let asprint pp x = asprintf "%a" pp x (* eta-expanded to bypass value restriction *) From c2db27d9de0bda2d9de7c91d89a7c03496421446 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 27 Jun 2024 12:36:49 +0300 Subject: [PATCH 270/566] Remove some trailing whitespace --- .../apron/linearTwoVarEqualityDomain.apron.ml | 24 +++++++++---------- src/cdomains/apron/sharedFunctions.apron.ml | 2 +- src/common/domains/printable.ml | 10 ++++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 5558bc2c96..3a9e2d3e11 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -158,7 +158,7 @@ module EqualitiesConjunction = struct let (newref,offs,divi) = (get_rhs d head) in let (coeff,y) = BatOption.get newref in let (y,yrhs) = inverse head (coeff,y,offs,divi) in (* reassemble yrhs out of components *) - let shifted_cluster = (List.fold (fun map i -> + let shifted_cluster = (List.fold (fun map i -> let irhs = (get_rhs d i) in (* old entry is i = irhs *) Rhs.subst yrhs y irhs |> (* new entry for i is irhs [yrhs/y] *) set_rhs map i @@ -222,7 +222,7 @@ module EqualitiesConjunction = struct | Some (coeff,j), ((Some (coeff1,h1), o1, divi1) as oldi)-> (match get_rhs ts j with (* ts[x_j]=o2/d2 ========> ... *) - | (None , o2, divi2) -> + | (None , o2, divi2) -> let newxi = Rhs.subst (None,o2,divi2) j (Some (coeff,j),offs,divi) in let newxh1 = snd @@ inverse i (coeff1,h1,o1,divi1) in let newxh1 = Rhs.subst newxi i newxh1 in @@ -251,7 +251,7 @@ module EqualitiesConjunction = struct else (* var_i = var_i, i.e. it may occur on the rhs of other equalities *) (* so now, we transform with the inverse of the transformer: *) let inv = snd (inverse i (coeff,j,offs,divi)) in - IntMap.fold (fun k v acc -> + IntMap.fold (fun k v acc -> match v with | (Some (c,x),o,d) when x=i-> set_rhs acc k (Rhs.subst inv i v) | _ -> acc @@ -281,7 +281,7 @@ struct let multiply a b = (* if one of them is a constant, then multiply. Otherwise, the expression is not linear *) match a, b with - | [(None,coeff, divi)], c + | [(None,coeff, divi)], c | c, [(None,coeff, divi)] -> multiply_with_Q coeff divi c | _ -> raise NotLinearExpr in @@ -314,7 +314,7 @@ struct | x -> Some(x) (** convert and simplify (wrt. reference variables) a texpr into a tuple of a list of monomials (coeff,varidx,divi) and a (constant/divi) *) - let simplified_monomials_from_texp (t: t) texp = + let simplified_monomials_from_texp (t: t) texp = BatOption.bind (monomials_from_texp t texp) (fun monomiallist -> let d = Option.get t.d in @@ -323,7 +323,7 @@ struct | None -> let gcdee = Z.gcd adiv divi in exprcache,(Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi) | Some (coeff,idx) -> let (somevar,someoffs,somedivi)=Rhs.subst (EConj.get_rhs d idx) idx (v,offs,divi) in (* normalize! *) let newcache = Option.map_default (fun (coef,ter) -> IMap.add ter Q.((IMap.find_default zero ter exprcache) + make coef somedivi) exprcache) exprcache somevar in - let gcdee = Z.gcd adiv divi in + let gcdee = Z.gcd adiv divi in (newcache,(Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi)) in let (expr,constant) = List.fold_left accumulate_constants (IMap.empty,(Z.zero,Z.one)) monomiallist in (* abstract simplification of the guard wrt. reference variables *) @@ -339,7 +339,7 @@ struct BatOption.bind (simplified_monomials_from_texp t texp ) (fun (sum_of_terms, (constant,divisor)) -> (match sum_of_terms with - | [] -> Some (None, constant,divisor) + | [] -> Some (None, constant,divisor) | [(coeff,var,divi)] -> Some (Rhs.canonicalize (Some (Z.mul divisor coeff,var), Z.mul constant divi,Z.mul divisor divi)) |_ -> None)) @@ -447,7 +447,7 @@ struct let t1 = change_d t1 sup_env ~add:true ~del:false in let t2 = change_d t2 sup_env ~add:true ~del:false in match t1.d, t2.d with - | Some d1', Some d2' -> + | Some d1', Some d2' -> EConj.IntMap.fold (fun lhs rhs map -> meet_with_one_conj map lhs rhs) (snd d2') t1 (* even on sparse d2, this will chose the relevant conjs to meet with*) | _ -> {d = None; env = sup_env} @@ -489,7 +489,7 @@ struct - lhs itself - criteria A and B that characterize equivalence class, depending on the reference variable and the affine expression parameters wrt. each EConj - rhs1 - - rhs2 + - rhs2 however, we have to account for the sparseity of EConj maps by manually patching holes with default values *) let joinfunction lhs rhs1 rhs2 = ( @@ -516,15 +516,15 @@ struct let varentry ci offi ch offh xh = let (coeff,off,d) = Q.(ci,(offi*ch)-(ci*offh),ch) in (* compute new rhs in Q *) let (coeff,off,d) = Z.(coeff.num*d.den*off.den,off.num*d.den*coeff.den,d. num*coeff.den*off.den) in (* convert that back into Z *) - Rhs.canonicalize (Some(coeff,xh),off,d) + Rhs.canonicalize (Some(coeff,xh),off,d) in (* ci1 = a*ch1+b /\ ci2 = a*ch2+b *) (* ===> a = (ci1-ci2)/(ch1-ch2) b = ci2-a*ch2 *) - let constentry ci1 ci2 ch1 ch2 xh = + let constentry ci1 ci2 ch1 ch2 xh = let a = Q.((ci1-ci2) / (ch1-ch2)) in let b = Q.(ci2 - a*ch2) in Rhs.canonicalize (Some (Z.(a.num*b.den),xh),Z.(b.num*a.den) ,Z.(a.den*b.den) ) in - let iterate map l = + let iterate map l = match l with | (_, _, _, rhs , rhs' ) :: t when Rhs.equal rhs rhs' -> List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l | (h, _, _, ((Some (ch,_),oh,dh)), ((Some _,_,_) )) :: t -> List.fold (fun acc (i,_,_,(monom,oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make (fst@@Option.get monom) di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 8c94c996b0..8167667293 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -136,7 +136,7 @@ struct let expr = (** simplify asks for a constant value of some subexpression e, similar to a constant fold. In particular but not exclusively this query is answered by the 2 var equalities domain itself. This normalizes arbitrary expressions to a point where they - might be able to be represented by means of 2 var equalities + might be able to be represented by means of 2 var equalities This simplification happens during a time, when there are temporary variables a#in and a#out part of the expression, but are not represented in the ctx, thus queries may result in top for these variables. Wrapping this in speculative diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index 1a642c932a..199113a5d8 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -588,7 +588,7 @@ module Prod4 (Base1: S) (Base2: S) (Base3: S) (Base4: S) = struct let arbitrary () = QCheck.quad (Base1.arbitrary ()) (Base2.arbitrary ()) (Base3.arbitrary ()) (Base4.arbitrary ()) end -module PQueue (Base: S) = +module PQueue (Base: S) = struct type t = Base.t BatDeque.dq include Std @@ -604,22 +604,22 @@ struct let rec loop n q = match BatDeque.front q with | None -> () - | Some (x, xs) -> (BatPrintf.fprintf f "%d\n%a\n" n Base.printXml x; + | Some (x, xs) -> (BatPrintf.fprintf f "%d\n%a\n" n Base.printXml x; loop (n+1) (xs)) in BatPrintf.fprintf f "\n\n"; - loop 0 xs; + loop 0 xs; BatPrintf.fprintf f "\n\n" let to_yojson q = `List (BatDeque.to_list @@ BatDeque.map (Base.to_yojson) q) let hash q = BatDeque.fold_left (fun acc x -> (acc + 71) * (Base.hash x)) 11 q let equal q1 q2 = BatDeque.eq ~eq:Base.equal q1 q2 - let compare q1 q2 = + let compare q1 q2 = match BatDeque.front q1, BatDeque.front q2 with | None, None -> 0 | None, Some(_, _) -> -1 | Some(_, _), None -> 1 - | Some(a1, q1'), Some(a2, q2') -> + | Some(a1, q1'), Some(a2, q2') -> let c = Base.compare a1 a2 in if c <> 0 then c else compare q1' q2' From 47b9f44d67539fed3fc534a22ea999d47d656417 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 28 Jun 2024 12:09:47 +0300 Subject: [PATCH 271/566] Add package description (closes #1528) --- dune-project | 5 +++++ goblint.opam | 5 +++++ goblint.opam.locked | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/dune-project b/dune-project index b85a82b93f..f07ccf4236 100644 --- a/dune-project +++ b/dune-project @@ -22,6 +22,11 @@ (package (name goblint) (synopsis "Static analysis framework for C") + (description "\ +Goblint is a sound static analysis framework for C programs using abstract interpretation. +It specializes in thread-modular verification of multi-threaded programs, especially regarding data races. +Goblint includes analyses for assertions, overflows, deadlocks, etc and can be extended with new analyses. +") (depends (ocaml (>= 4.14)) (goblint-cil (>= 2.0.3)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. diff --git a/goblint.opam b/goblint.opam index 03e11d456a..eec74439a0 100644 --- a/goblint.opam +++ b/goblint.opam @@ -1,6 +1,11 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "Static analysis framework for C" +description: """ +Goblint is a sound static analysis framework for C programs using abstract interpretation. +It specializes in thread-modular verification of multi-threaded programs, especially regarding data races. +Goblint includes analyses for assertions, overflows, deadlocks, etc and can be extended with new analyses. +""" maintainer: [ "Simmo Saan " "Michael Schwarz " diff --git a/goblint.opam.locked b/goblint.opam.locked index 0cf9a2ff75..90bfba68f0 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -143,3 +143,7 @@ pin-depends: [ ] ] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} +description: """\ +Goblint is a sound static analysis framework for C programs using abstract interpretation. +It specializes in thread-modular verification of multi-threaded programs, especially regarding data races. +Goblint includes analyses for assertions, overflows, deadlocks, etc and can be extended with new analyses.""" From c078eaa015ef54bf70b4d0598dea46ef1675c70e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 28 Jun 2024 12:19:19 +0300 Subject: [PATCH 272/566] Add package tags --- dune-project | 8 ++++++++ goblint.opam | 9 +++++++++ goblint.opam.locked | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/dune-project b/dune-project index f07ccf4236..8d9d53623b 100644 --- a/dune-project +++ b/dune-project @@ -27,6 +27,14 @@ Goblint is a sound static analysis framework for C programs using abstract inter It specializes in thread-modular verification of multi-threaded programs, especially regarding data races. Goblint includes analyses for assertions, overflows, deadlocks, etc and can be extended with new analyses. ") + (tags ( + "program analysis" + "program verification" + "static analysis" + "abstract interpretation" + "C" + "data race analysis" + "concurrency")) (depends (ocaml (>= 4.14)) (goblint-cil (>= 2.0.3)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. diff --git a/goblint.opam b/goblint.opam index eec74439a0..e198bf6f3c 100644 --- a/goblint.opam +++ b/goblint.opam @@ -22,6 +22,15 @@ authors: [ "Vesal Vojdani" ] license: "MIT" +tags: [ + "program analysis" + "program verification" + "static analysis" + "abstract interpretation" + "C" + "data race analysis" + "concurrency" +] homepage: "https://goblint.in.tum.de" doc: "https://goblint.readthedocs.io/en/latest/" bug-reports: "https://github.com/goblint/analyzer/issues" diff --git a/goblint.opam.locked b/goblint.opam.locked index 90bfba68f0..b1d8030c1c 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -147,3 +147,12 @@ description: """\ Goblint is a sound static analysis framework for C programs using abstract interpretation. It specializes in thread-modular verification of multi-threaded programs, especially regarding data races. Goblint includes analyses for assertions, overflows, deadlocks, etc and can be extended with new analyses.""" +tags: [ + "program analysis" + "program verification" + "static analysis" + "abstract interpretation" + "C" + "data race analysis" + "concurrency" +] From 4fe33dcf25c095acd6e2a561ffa779e7d9b87a93 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Fri, 28 Jun 2024 11:22:34 +0200 Subject: [PATCH 273/566] make sure that exact division is possible --- src/cdomains/apron/sharedFunctions.apron.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 022e60200d..e46f8b2f4b 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -33,7 +33,9 @@ let int_of_scalar ?(scalewith=Z.one) ?round (scalar: Scalar.t) = begin match round with | Some `Floor -> Some (Mpzf.mul scale (Mpzf.fdiv_q n d)) (* floor division *) | Some `Ceil -> Some (Mpzf.mul scale (Mpzf.cdiv_q n d)) (* ceiling division *) - | None -> Some (Mpzf.divexact (Mpzf.mul scale n ) d) (* scale, preferably with common denominator *) + | None -> if Mpz.divisible_p (Mpzf.mul scale n ) d then + Some (Mpzf.divexact (Mpzf.mul scale n ) d) (* scale, preferably with common denominator *) + else None end in Z_mlgmpidl.z_of_mpzf z From 8ddbc43a728260596995cf9ecfb9cbbb6049171f Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 28 Jun 2024 15:44:24 +0300 Subject: [PATCH 274/566] Update results table in relationalAnalysis only if exp.relation.prec-dump is set --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index a42d78d71a..ad99e26b58 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -756,7 +756,7 @@ struct let sync ctx reason = (* After the solver is finished, store the results (for later comparison) *) - if !AnalysisState.postsolving then begin + if !AnalysisState.postsolving && GobConfig.get_string "exp.relation.prec-dump" <> "" then begin let keep_local = GobConfig.get_bool "ana.relation.invariant.local" in let keep_global = GobConfig.get_bool "ana.relation.invariant.global" in From 4d5a4085c67090a173ffdeb56d20b3cd0a1f5143 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 2 Jul 2024 11:06:30 +0200 Subject: [PATCH 275/566] removed unused parameter --- src/cdomains/apron/sharedFunctions.apron.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index e46f8b2f4b..9652942136 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -300,14 +300,14 @@ struct | Mpqf f -> Some (Z_mlgmpidl.q_of_mpqf f) | _ -> raise UnsupportedScalar in - let extract_den (c:Coeff.union_5) _ = + let extract_den (c:Coeff.union_5) = match c with | Scalar c -> BatOption.map Q.den (frac_of_scalar c) | _ -> None in - let lcm_denom = ref (BatOption.default Z.one (extract_den (Linexpr1.get_cst linexpr1) ())) in - let lcm_coeff (c:Coeff.union_5) v = - match (extract_den c v) with + let lcm_denom = ref (BatOption.default Z.one (extract_den (Linexpr1.get_cst linexpr1))) in + let lcm_coeff (c:Coeff.union_5) _ = + match (extract_den c) with | Some z -> lcm_denom := Z.lcm z !lcm_denom | _ -> () in From 05dfb9fcc0dfb75659767fc6d210db08023eb514 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 2 Jul 2024 13:55:47 +0200 Subject: [PATCH 276/566] pulled out product --- src/cdomains/apron/sharedFunctions.apron.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 9652942136..668b6af5d4 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -33,8 +33,8 @@ let int_of_scalar ?(scalewith=Z.one) ?round (scalar: Scalar.t) = begin match round with | Some `Floor -> Some (Mpzf.mul scale (Mpzf.fdiv_q n d)) (* floor division *) | Some `Ceil -> Some (Mpzf.mul scale (Mpzf.cdiv_q n d)) (* ceiling division *) - | None -> if Mpz.divisible_p (Mpzf.mul scale n ) d then - Some (Mpzf.divexact (Mpzf.mul scale n ) d) (* scale, preferably with common denominator *) + | None -> let product = Mpzf.mul scale n in if Mpz.divisible_p product d then + Some (Mpzf.divexact product d) (* scale, preferably with common denominator *) else None end in From dd2932f56bbabc1da077206e7cbae19f69d301d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 20:16:10 +0000 Subject: [PATCH 277/566] Bump ocaml/setup-ocaml from 2 to 3 Bumps [ocaml/setup-ocaml](https://github.com/ocaml/setup-ocaml) from 2 to 3. - [Release notes](https://github.com/ocaml/setup-ocaml/releases) - [Changelog](https://github.com/ocaml/setup-ocaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/ocaml/setup-ocaml/compare/v2...v3) --- updated-dependencies: - dependency-name: ocaml/setup-ocaml dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/coverage.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/indentation.yml | 2 +- .github/workflows/locked.yml | 6 +++--- .github/workflows/unlocked.yml | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index d4fc872620..245c7eb23b 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -35,7 +35,7 @@ jobs: # otherwise setup-ocaml pins non-locked dependencies # https://github.com/ocaml/setup-ocaml/issues/166 OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 242acef3de..253c7514b4 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -35,7 +35,7 @@ jobs: # otherwise setup-ocaml pins non-locked dependencies # https://github.com/ocaml/setup-ocaml/issues/166 OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} diff --git a/.github/workflows/indentation.yml b/.github/workflows/indentation.yml index e22e674301..96ef5ee56a 100644 --- a/.github/workflows/indentation.yml +++ b/.github/workflows/indentation.yml @@ -25,7 +25,7 @@ jobs: fetch-depth: 0 - name: Set up OCaml ${{ matrix.ocaml-compiler }} - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 18935725ca..5f795fcf67 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -37,7 +37,7 @@ jobs: # otherwise setup-ocaml pins non-locked dependencies # https://github.com/ocaml/setup-ocaml/issues/166 OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) @@ -87,7 +87,7 @@ jobs: # otherwise setup-ocaml pins non-locked dependencies # https://github.com/ocaml/setup-ocaml/issues/166 OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) @@ -132,7 +132,7 @@ jobs: # otherwise setup-ocaml pins non-locked dependencies # https://github.com/ocaml/setup-ocaml/issues/166 OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index f1bd399a17..935bcfab25 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -45,7 +45,7 @@ jobs: uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) @@ -106,7 +106,7 @@ jobs: uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) @@ -199,7 +199,7 @@ jobs: uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) From d1663cfc3e073dda53f9f7d328af68cd4b727729 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 3 Jul 2024 11:39:55 +0300 Subject: [PATCH 278/566] Remove opam-depext-flags from ocaml/setup-ocaml@v3 It has been removed now and didn't work anyway. --- .github/workflows/coverage.yml | 1 - .github/workflows/locked.yml | 2 -- .github/workflows/unlocked.yml | 3 --- 3 files changed, 6 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 245c7eb23b..47feb77d0b 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -38,7 +38,6 @@ jobs: uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works if: ${{ matrix.os == 'ubuntu-latest' }} diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 5f795fcf67..dbcb46635a 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -40,7 +40,6 @@ jobs: uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works if: ${{ matrix.os == 'ubuntu-latest' }} @@ -90,7 +89,6 @@ jobs: uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works if: ${{ matrix.os == 'ubuntu-latest' }} diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 935bcfab25..60a9003c16 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -48,7 +48,6 @@ jobs: uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works if: ${{ matrix.os == 'ubuntu-latest' }} @@ -109,7 +108,6 @@ jobs: uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works if: ${{ matrix.os == 'ubuntu-latest' }} @@ -202,7 +200,6 @@ jobs: uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works if: ${{ matrix.os == 'ubuntu-latest' }} From 502cf9f7b7ad704025270a6b8a6df1570927783a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 4 Jul 2024 11:45:38 +0300 Subject: [PATCH 279/566] Use setup-ocaml@v2 for lower-bounds CI opam-0install is incompatible with opam 2.2 --- .github/workflows/unlocked.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 60a9003c16..e4be79e630 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -105,7 +105,7 @@ jobs: uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} - uses: ocaml/setup-ocaml@v3 + uses: ocaml/setup-ocaml@v2 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} From 71a1657124fd4f9fb12e954f199189f8ef6e5244 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 5 Jul 2024 13:09:03 +0300 Subject: [PATCH 280/566] Remove apron v0.9.15 pin --- goblint.opam | 1 - goblint.opam.locked | 4 ---- goblint.opam.template | 1 - 3 files changed, 6 deletions(-) diff --git a/goblint.opam b/goblint.opam index ec6a1f8fcb..0cc5eab3a2 100644 --- a/goblint.opam +++ b/goblint.opam @@ -84,7 +84,6 @@ pin-depends: [ [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] - [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#b3172bd51e4bb0135716a300cab424a56970d96f" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.locked b/goblint.opam.locked index 8b184e189d..532b8e1aff 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -140,9 +140,5 @@ pin-depends: [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] - [ - "apron.v0.9.15" - "git+https://github.com/antoinemine/apron.git#b3172bd51e4bb0135716a300cab424a56970d96f" - ] ] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.template b/goblint.opam.template index 27be21015f..2d5ef10bc9 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -6,7 +6,6 @@ pin-depends: [ [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] - [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#b3172bd51e4bb0135716a300cab424a56970d96f" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} From a23b728a4023b9c8582c852b7f6d31ceb4f8b328 Mon Sep 17 00:00:00 2001 From: Fabian Stemmler Date: Thu, 11 Jul 2024 11:29:39 +0200 Subject: [PATCH 281/566] propagate bottom through narrowing operator of lifters --- src/domain/lattice.ml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/domain/lattice.ml b/src/domain/lattice.ml index 99322c09d8..d45ddcbfc2 100644 --- a/src/domain/lattice.ml +++ b/src/domain/lattice.ml @@ -277,6 +277,8 @@ struct let narrow x y = match (x,y) with | (`Lifted x, `Lifted y) -> `Lifted (Base.narrow x y) + | (_, `Bot) -> `Bot + | (`Top, y) -> y | _ -> x end @@ -337,6 +339,8 @@ struct | (`Lifted x, `Lifted y) -> (try `Lifted (Base.narrow x y) with Uncomparable -> `Bot) + | (_, `Bot) -> `Bot + | (`Top, y) -> y | _ -> x end @@ -408,6 +412,8 @@ struct match (x,y) with | (`Lifted1 x, `Lifted1 y) -> `Lifted1 (Base1.narrow x y) | (`Lifted2 x, `Lifted2 y) -> `Lifted2 (Base2.narrow x y) + | (_, `Bot) -> `Bot + | (`Top, y) -> y | _ -> x end @@ -539,6 +545,7 @@ struct let narrow x y = match (x,y) with | (`Lifted x, `Lifted y) -> `Lifted (Base.narrow x y) + | (_, `Bot) -> `Bot | _ -> x end @@ -580,6 +587,7 @@ struct let narrow x y = match (x,y) with | (`Lifted x, `Lifted y) -> `Lifted (Base.narrow x y) + | (`Top, y) -> y | _ -> x let pretty_diff () (x,y) = From 11ed4180e3064e4d43fb01751df553e233235a77 Mon Sep 17 00:00:00 2001 From: Jonas August Date: Thu, 29 Jun 2023 08:29:30 +0200 Subject: [PATCH 282/566] Fix depreciated option --- tests/regression/46-apron2/26-autotune.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/46-apron2/26-autotune.c b/tests/regression/46-apron2/26-autotune.c index 96c5c85278..ec8f55fe19 100644 --- a/tests/regression/46-apron2/26-autotune.c +++ b/tests/regression/46-apron2/26-autotune.c @@ -1,4 +1,4 @@ -//SKIP PARAM: --enable ana.int.interval --sets sem.int.signed_overflow assume_none --set ana.activated[+] apron --enable ana.autotune.enabled +//SKIP PARAM: --enable ana.int.interval --set sem.int.signed_overflow assume_none --set ana.activated[+] apron --enable ana.autotune.enabled // Check that autotuner disables context for apron as well for recursive calls #include From 0077e1d78857f238aae4fb87a0d7b0ef5aba5b9c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Jul 2024 15:09:51 +0300 Subject: [PATCH 283/566] Replace --sets with --set in tests --- scripts/creduce/privPrecCompare.sh | 2 +- scripts/privPrecCompare.sh | 2 +- tests/regression/01-cpa/71-widen-sides.c | 2 +- tests/regression/46-apron2/03-other-assume.c | 2 +- tests/regression/46-apron2/04-other-assume-inprec.c | 2 +- tests/regression/46-apron2/30-autotune-stub.c | 2 +- tests/regression/58-base-mm-tid/01-tid-toy1.c | 2 +- tests/regression/58-base-mm-tid/22-other-assume.c | 2 +- tests/regression/58-base-mm-tid/23-other-assume-inprec.c | 2 +- tests/regression/66-interval-set-one/51-widen-sides.c | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/creduce/privPrecCompare.sh b/scripts/creduce/privPrecCompare.sh index fdc5f9219d..2165112924 100755 --- a/scripts/creduce/privPrecCompare.sh +++ b/scripts/creduce/privPrecCompare.sh @@ -22,7 +22,7 @@ for PRIV in "${PRIVS[@]}"; do PRIVDUMP="$OUTDIR/$PRIV" LOG="$OUTDIR/$PRIV.log" rm -f $PRIVDUMP - $GOBLINTDIR/goblint --sets exp.privatization $PRIV --sets exp.priv-prec-dump $PRIVDUMP $OPTS -v --enable warn.debug &> $LOG + $GOBLINTDIR/goblint --set exp.privatization $PRIV --set exp.priv-prec-dump $PRIVDUMP $OPTS -v --enable warn.debug &> $LOG grep -F "Function definition missing" $LOG && exit 1 done diff --git a/scripts/privPrecCompare.sh b/scripts/privPrecCompare.sh index c5b9f2c01e..2b09fb80b5 100755 --- a/scripts/privPrecCompare.sh +++ b/scripts/privPrecCompare.sh @@ -10,7 +10,7 @@ mkdir -p $OUTDIR for PRIV in "${PRIVS[@]}"; do echo $PRIV PRIVDUMP="$OUTDIR/$PRIV" - ./goblint --sets exp.privatization $PRIV --sets exp.priv-prec-dump $PRIVDUMP "$@" + ./goblint --set exp.privatization $PRIV --set exp.priv-prec-dump $PRIVDUMP "$@" done PRIVDUMPS=("${PRIVS[*]/#/$OUTDIR/}") # why [*] here? diff --git a/tests/regression/01-cpa/71-widen-sides.c b/tests/regression/01-cpa/71-widen-sides.c index 522f6fae44..a93124c6e5 100644 --- a/tests/regression/01-cpa/71-widen-sides.c +++ b/tests/regression/01-cpa/71-widen-sides.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.ctx_insens "['base', 'mallocWrapper']" --enable ana.int.interval --sets solvers.td3.side_widen sides-local +// PARAM: --set ana.ctx_insens "['base', 'mallocWrapper']" --enable ana.int.interval --set solvers.td3.side_widen sides-local #include int further(int n) { diff --git a/tests/regression/46-apron2/03-other-assume.c b/tests/regression/46-apron2/03-other-assume.c index 635911a197..c136d1bde3 100644 --- a/tests/regression/46-apron2/03-other-assume.c +++ b/tests/regression/46-apron2/03-other-assume.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.activated[+] threadJoins --sets ana.relation.privatization mutex-meet-tid +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.activated[+] threadJoins --set ana.relation.privatization mutex-meet-tid #include #include diff --git a/tests/regression/46-apron2/04-other-assume-inprec.c b/tests/regression/46-apron2/04-other-assume-inprec.c index 09b8d150a8..ccf8ecefa1 100644 --- a/tests/regression/46-apron2/04-other-assume-inprec.c +++ b/tests/regression/46-apron2/04-other-assume-inprec.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.activated[+] threadJoins --sets ana.relation.privatization mutex-meet-tid +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.activated[+] threadJoins --set ana.relation.privatization mutex-meet-tid #include #include diff --git a/tests/regression/46-apron2/30-autotune-stub.c b/tests/regression/46-apron2/30-autotune-stub.c index e1b7603c3b..02c80e7d21 100644 --- a/tests/regression/46-apron2/30-autotune-stub.c +++ b/tests/regression/46-apron2/30-autotune-stub.c @@ -1,4 +1,4 @@ -//SKIP PARAM: --enable ana.int.interval --sets sem.int.signed_overflow assume_none --set ana.activated[+] apron --enable ana.autotune.enabled +//SKIP PARAM: --enable ana.int.interval --set sem.int.signed_overflow assume_none --set ana.activated[+] apron --enable ana.autotune.enabled // Check that autotuner respect goblint_stub attributes as hints to not track variables. #include diff --git a/tests/regression/58-base-mm-tid/01-tid-toy1.c b/tests/regression/58-base-mm-tid/01-tid-toy1.c index bc3cd18efa..49e96881d1 100644 --- a/tests/regression/58-base-mm-tid/01-tid-toy1.c +++ b/tests/regression/58-base-mm-tid/01-tid-toy1.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.path_sens[+] threadflag --sets ana.base.privatization mutex-meet-tid +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid // Inspired by 36/71 #include #include diff --git a/tests/regression/58-base-mm-tid/22-other-assume.c b/tests/regression/58-base-mm-tid/22-other-assume.c index 4f5c73b038..301195ac1f 100644 --- a/tests/regression/58-base-mm-tid/22-other-assume.c +++ b/tests/regression/58-base-mm-tid/22-other-assume.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.path_sens[+] threadflag --sets ana.base.privatization mutex-meet-tid +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid // Copy of 46/03 for base #include #include diff --git a/tests/regression/58-base-mm-tid/23-other-assume-inprec.c b/tests/regression/58-base-mm-tid/23-other-assume-inprec.c index 37e59d3edf..a4bac04c59 100644 --- a/tests/regression/58-base-mm-tid/23-other-assume-inprec.c +++ b/tests/regression/58-base-mm-tid/23-other-assume-inprec.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.path_sens[+] threadflag --sets ana.base.privatization mutex-meet-tid --set ana.activated[+] threadJoins +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --set ana.activated[+] threadJoins // Copy of 46/04 for base #include #include diff --git a/tests/regression/66-interval-set-one/51-widen-sides.c b/tests/regression/66-interval-set-one/51-widen-sides.c index b086baf026..53fe749704 100644 --- a/tests/regression/66-interval-set-one/51-widen-sides.c +++ b/tests/regression/66-interval-set-one/51-widen-sides.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.ctx_insens "['base', 'mallocWrapper']" --enable ana.int.interval_set --sets solvers.td3.side_widen sides-local +// PARAM: --set ana.ctx_insens "['base', 'mallocWrapper']" --enable ana.int.interval_set --set solvers.td3.side_widen sides-local #include int further(int n) { From 67c18668154d79ce5cdd71c887788c8cfa09aab0 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Fri, 8 Dec 2023 14:26:31 +0100 Subject: [PATCH 284/566] change may fail to fail annotation --- tests/regression/36-apron/86-branched-thread-creation.c | 2 +- tests/regression/58-base-mm-tid/15-branched-thread-creation.c | 2 +- .../67-interval-sets-two/16-branched-thread-creation.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/regression/36-apron/86-branched-thread-creation.c b/tests/regression/36-apron/86-branched-thread-creation.c index cac0a881a6..91a5411e8f 100644 --- a/tests/regression/36-apron/86-branched-thread-creation.c +++ b/tests/regression/36-apron/86-branched-thread-creation.c @@ -40,7 +40,7 @@ int main(void) { if(!mt) { pthread_mutex_lock(&mutex); - __goblint_check(g==h); //MAYFAIL + __goblint_check(g==h); //FAIL pthread_mutex_unlock(&mutex); } diff --git a/tests/regression/58-base-mm-tid/15-branched-thread-creation.c b/tests/regression/58-base-mm-tid/15-branched-thread-creation.c index 3292182adc..c7bb43519f 100644 --- a/tests/regression/58-base-mm-tid/15-branched-thread-creation.c +++ b/tests/regression/58-base-mm-tid/15-branched-thread-creation.c @@ -41,7 +41,7 @@ int main(void) { if(!mt) { pthread_mutex_lock(&mutex); - __goblint_check(g==h); //MAYFAIL + __goblint_check(g==h); //FAIL pthread_mutex_unlock(&mutex); } diff --git a/tests/regression/67-interval-sets-two/16-branched-thread-creation.c b/tests/regression/67-interval-sets-two/16-branched-thread-creation.c index c309ec8ffd..d97f800621 100644 --- a/tests/regression/67-interval-sets-two/16-branched-thread-creation.c +++ b/tests/regression/67-interval-sets-two/16-branched-thread-creation.c @@ -41,7 +41,7 @@ int main(void) { if(!mt) { pthread_mutex_lock(&mutex); - __goblint_check(g==h); //MAYFAIL + __goblint_check(g==h); //FAIL pthread_mutex_unlock(&mutex); } From f1c6e2c6d4acf94a2137433d34364c8c639c9aec Mon Sep 17 00:00:00 2001 From: J2000A Date: Fri, 5 May 2023 14:41:52 +0200 Subject: [PATCH 285/566] Adjust evalAssert to offer __goblint_check(exp) with the option trans.goblint-check --- src/config/options.schema.json | 6 ++++++ src/transform/evalAssert.ml | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index d259a6f418..2e2a9212ef 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -1561,6 +1561,12 @@ "description": "Output filename for transformations that output a transformed file.", "type":"string", "default": "transformed.c" + }, + "goblint-check" : { + "title": "trans.goblint-check", + "description": "Write __Goblint_Check(exp) in the file instead of __VERIFIER_assert(exp).", + "type": "boolean", + "default": false } }, "additionalProperties": false diff --git a/src/transform/evalAssert.ml b/src/transform/evalAssert.ml index eab06222ef..82b92b9040 100644 --- a/src/transform/evalAssert.ml +++ b/src/transform/evalAssert.ml @@ -26,8 +26,11 @@ module EvalAssert = struct (* should asserts be surrounded by __VERIFIER_atomic_{begin,end}? *) let surroundByAtomic = true + (* variable for generating __goblint_check(exp) instead of __VERIFIER_assert(exp)*) + let goblintCheck = GobConfig.get_bool "trans.goblint-check" + (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) - let ass = makeVarinfo true "__VERIFIER_assert" (TVoid []) + let ass = if goblintCheck then makeVarinfo true "__goblint_check" (TVoid []) else makeVarinfo true "__VERIFIER_assert" (TVoid []) let atomicBegin = makeVarinfo true "__VERIFIER_atomic_begin" (TVoid []) let atomicEnd = makeVarinfo true "__VERIFIER_atomic_end" (TVoid []) @@ -60,7 +63,7 @@ module EvalAssert = struct | `Lifted e -> let es = WitnessUtil.InvariantExp.process_exp e in let asserts = List.map (fun e -> cInstr ("%v:assert (%e:exp);") loc [("assert", Fv ass); ("exp", Fe e)]) es in - if surroundByAtomic then + if surroundByAtomic && not goblintCheck then let abegin = (cInstr ("%v:__VERIFIER_atomic_begin();") loc [("__VERIFIER_atomic_begin", Fv atomicBegin)]) in let aend = (cInstr ("%v:__VERIFIER_atomic_end();") loc [("__VERIFIER_atomic_end", Fv atomicEnd)]) in abegin :: (asserts @ [aend]) From 8db9e1d320de69baf396bab7b56f55149f577a69 Mon Sep 17 00:00:00 2001 From: Jonas August Date: Wed, 10 May 2023 19:24:34 +0200 Subject: [PATCH 286/566] Adopt evalAssert.ml so the option is checked correctly at runtime --- src/transform/evalAssert.ml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/transform/evalAssert.ml b/src/transform/evalAssert.ml index 82b92b9040..fb9cf84d86 100644 --- a/src/transform/evalAssert.ml +++ b/src/transform/evalAssert.ml @@ -26,11 +26,6 @@ module EvalAssert = struct (* should asserts be surrounded by __VERIFIER_atomic_{begin,end}? *) let surroundByAtomic = true - (* variable for generating __goblint_check(exp) instead of __VERIFIER_assert(exp)*) - let goblintCheck = GobConfig.get_bool "trans.goblint-check" - - (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) - let ass = if goblintCheck then makeVarinfo true "__goblint_check" (TVoid []) else makeVarinfo true "__VERIFIER_assert" (TVoid []) let atomicBegin = makeVarinfo true "__VERIFIER_atomic_begin" (TVoid []) let atomicEnd = makeVarinfo true "__VERIFIER_atomic_end" (TVoid []) @@ -41,6 +36,8 @@ module EvalAssert = struct (* TODO: handle witness.invariant.loop-head *) val emit_after_lock = GobConfig.get_bool "witness.invariant.after-lock" val emit_other = GobConfig.get_bool "witness.invariant.other" + (* variable for generating __goblint_check(exp) instead of __VERIFIER_assert(exp) *) + val goblintCheck = GobConfig.get_bool "trans.goblint-check" method! vstmt s = let is_lock exp args = @@ -53,6 +50,10 @@ module EvalAssert = struct | _ -> false in + (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) + (* Moved in method because Options can not be checked in the module *) + let ass = if goblintCheck then makeVarinfo true "__goblint_check" (TVoid []) else makeVarinfo true "__VERIFIER_assert" (TVoid []) in + let make_assert ~node loc lval = let lvals = match lval with | None -> Lval.Set.top () From 21d804a0a1ea9337075be413d30ec02cdfd5e274 Mon Sep 17 00:00:00 2001 From: Jonas August Date: Tue, 16 May 2023 18:00:31 +0200 Subject: [PATCH 287/566] Fix Goblint Option Retrieving by adding Unit to constant --- src/transform/evalAssert.ml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/transform/evalAssert.ml b/src/transform/evalAssert.ml index fb9cf84d86..7fc70531db 100644 --- a/src/transform/evalAssert.ml +++ b/src/transform/evalAssert.ml @@ -26,6 +26,11 @@ module EvalAssert = struct (* should asserts be surrounded by __VERIFIER_atomic_{begin,end}? *) let surroundByAtomic = true + (* variable for generating __goblint_check(exp) instead of __VERIFIER_assert(exp) *) + let goblintCheck () = GobConfig.get_bool "trans.goblint-check" + + (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) + let ass () = if goblintCheck then makeVarinfo true "__goblint_check" (TVoid []) else makeVarinfo true "__VERIFIER_assert" (TVoid []) in let atomicBegin = makeVarinfo true "__VERIFIER_atomic_begin" (TVoid []) let atomicEnd = makeVarinfo true "__VERIFIER_atomic_end" (TVoid []) @@ -36,8 +41,6 @@ module EvalAssert = struct (* TODO: handle witness.invariant.loop-head *) val emit_after_lock = GobConfig.get_bool "witness.invariant.after-lock" val emit_other = GobConfig.get_bool "witness.invariant.other" - (* variable for generating __goblint_check(exp) instead of __VERIFIER_assert(exp) *) - val goblintCheck = GobConfig.get_bool "trans.goblint-check" method! vstmt s = let is_lock exp args = @@ -50,10 +53,6 @@ module EvalAssert = struct | _ -> false in - (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) - (* Moved in method because Options can not be checked in the module *) - let ass = if goblintCheck then makeVarinfo true "__goblint_check" (TVoid []) else makeVarinfo true "__VERIFIER_assert" (TVoid []) in - let make_assert ~node loc lval = let lvals = match lval with | None -> Lval.Set.top () From 69a33364e018a4aaea787b8aa4bda58441bd5b41 Mon Sep 17 00:00:00 2001 From: Jonas August Date: Tue, 23 May 2023 20:48:36 +0200 Subject: [PATCH 288/566] Bugfix --- src/transform/evalAssert.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/transform/evalAssert.ml b/src/transform/evalAssert.ml index 7fc70531db..56f3d5eb5f 100644 --- a/src/transform/evalAssert.ml +++ b/src/transform/evalAssert.ml @@ -30,7 +30,7 @@ module EvalAssert = struct let goblintCheck () = GobConfig.get_bool "trans.goblint-check" (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) - let ass () = if goblintCheck then makeVarinfo true "__goblint_check" (TVoid []) else makeVarinfo true "__VERIFIER_assert" (TVoid []) in + let ass () = if goblintCheck () then makeVarinfo true "__goblint_check" (TVoid []) else makeVarinfo true "__VERIFIER_assert" (TVoid []) let atomicBegin = makeVarinfo true "__VERIFIER_atomic_begin" (TVoid []) let atomicEnd = makeVarinfo true "__VERIFIER_atomic_end" (TVoid []) @@ -62,8 +62,8 @@ module EvalAssert = struct match (ask ~node loc).f (Queries.Invariant context) with | `Lifted e -> let es = WitnessUtil.InvariantExp.process_exp e in - let asserts = List.map (fun e -> cInstr ("%v:assert (%e:exp);") loc [("assert", Fv ass); ("exp", Fe e)]) es in - if surroundByAtomic && not goblintCheck then + let asserts = List.map (fun e -> cInstr ("%v:assert (%e:exp);") loc [("assert", Fv (ass ())); ("exp", Fe e)]) es in + if surroundByAtomic && not (goblintCheck ()) then let abegin = (cInstr ("%v:__VERIFIER_atomic_begin();") loc [("__VERIFIER_atomic_begin", Fv atomicBegin)]) in let aend = (cInstr ("%v:__VERIFIER_atomic_end();") loc [("__VERIFIER_atomic_end", Fv atomicEnd)]) in abegin :: (asserts @ [aend]) From 3e3c1ebda3871a079fbbe65c4b84828fdbbfc914 Mon Sep 17 00:00:00 2001 From: stilscher <66023521+stilscher@users.noreply.github.com> Date: Fri, 8 Dec 2023 14:37:44 +0100 Subject: [PATCH 289/566] rename option --- src/config/options.schema.json | 18 +++++++++++++----- src/transform/evalAssert.ml | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 2e2a9212ef..d808f121c3 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -1562,11 +1562,19 @@ "type":"string", "default": "transformed.c" }, - "goblint-check" : { - "title": "trans.goblint-check", - "description": "Write __Goblint_Check(exp) in the file instead of __VERIFIER_assert(exp).", - "type": "boolean", - "default": false + "assert" : { + "title": "trans.assert", + "type": "object", + "properties": { + "goblint-check": { + "title": "trans.assert.goblint-check", + "description": + "Write __goblint_check(exp) in the file instead of __VERIFIER_assert(exp).", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/src/transform/evalAssert.ml b/src/transform/evalAssert.ml index 56f3d5eb5f..94fdfb8602 100644 --- a/src/transform/evalAssert.ml +++ b/src/transform/evalAssert.ml @@ -27,7 +27,7 @@ module EvalAssert = struct let surroundByAtomic = true (* variable for generating __goblint_check(exp) instead of __VERIFIER_assert(exp) *) - let goblintCheck () = GobConfig.get_bool "trans.goblint-check" + let goblintCheck () = GobConfig.get_bool "trans.assert.goblint-check" (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) let ass () = if goblintCheck () then makeVarinfo true "__goblint_check" (TVoid []) else makeVarinfo true "__VERIFIER_assert" (TVoid []) From cae69f7006fa7c2f481e48d8613bc4f62a40b466 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Jul 2024 15:46:32 +0300 Subject: [PATCH 290/566] Refactor trans.assert options --- src/config/options.schema.json | 16 +++++++++++----- src/transform/evalAssert.ml | 20 +++++++++----------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index d808f121c3..0cb1b6ee67 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -1566,12 +1566,18 @@ "title": "trans.assert", "type": "object", "properties": { - "goblint-check": { - "title": "trans.assert.goblint-check", - "description": - "Write __goblint_check(exp) in the file instead of __VERIFIER_assert(exp).", + "function": { + "title": "trans.assert.function", + "description": "Function to use for assertions in output.", + "type": "string", + "enum": ["assert", "__goblint_check", "__VERIFIER_assert"], + "default": "__VERIFIER_assert" + }, + "wrap-atomic": { + "title": "trans.assert.wrap-atomic", + "description": "Wrap assertions in __VERIFIER_atomic_begin and __VERIFIER_atomic_end.", "type": "boolean", - "default": false + "default": true } }, "additionalProperties": false diff --git a/src/transform/evalAssert.ml b/src/transform/evalAssert.ml index 94fdfb8602..0fee26355b 100644 --- a/src/transform/evalAssert.ml +++ b/src/transform/evalAssert.ml @@ -22,15 +22,8 @@ open Formatcil will be removed and they will fail on the next iteration *) -module EvalAssert = struct - (* should asserts be surrounded by __VERIFIER_atomic_{begin,end}? *) - let surroundByAtomic = true - - (* variable for generating __goblint_check(exp) instead of __VERIFIER_assert(exp) *) - let goblintCheck () = GobConfig.get_bool "trans.assert.goblint-check" - - (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) - let ass () = if goblintCheck () then makeVarinfo true "__goblint_check" (TVoid []) else makeVarinfo true "__VERIFIER_assert" (TVoid []) +module EvalAssert = +struct let atomicBegin = makeVarinfo true "__VERIFIER_atomic_begin" (TVoid []) let atomicEnd = makeVarinfo true "__VERIFIER_atomic_end" (TVoid []) @@ -42,6 +35,11 @@ module EvalAssert = struct val emit_after_lock = GobConfig.get_bool "witness.invariant.after-lock" val emit_other = GobConfig.get_bool "witness.invariant.other" + (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) + val assert_function = makeVarinfo true (GobConfig.get_string "trans.assert.function") (TVoid []) + (* should asserts be surrounded by __VERIFIER_atomic_{begin,end}? *) + val surroundByAtomic = GobConfig.get_bool "trans.assert.wrap-atomic" + method! vstmt s = let is_lock exp args = match exp with @@ -62,8 +60,8 @@ module EvalAssert = struct match (ask ~node loc).f (Queries.Invariant context) with | `Lifted e -> let es = WitnessUtil.InvariantExp.process_exp e in - let asserts = List.map (fun e -> cInstr ("%v:assert (%e:exp);") loc [("assert", Fv (ass ())); ("exp", Fe e)]) es in - if surroundByAtomic && not (goblintCheck ()) then + let asserts = List.map (fun e -> cInstr ("%v:assert (%e:exp);") loc [("assert", Fv assert_function); ("exp", Fe e)]) es in + if surroundByAtomic then let abegin = (cInstr ("%v:__VERIFIER_atomic_begin();") loc [("__VERIFIER_atomic_begin", Fv atomicBegin)]) in let aend = (cInstr ("%v:__VERIFIER_atomic_end();") loc [("__VERIFIER_atomic_end", Fv atomicEnd)]) in abegin :: (asserts @ [aend]) From 8971af4da76b730fb3fe0eea624cd2a551561d44 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Jul 2024 17:59:53 +0300 Subject: [PATCH 291/566] Add NOCHECK annotation to suppress missing automatic check warning --- scripts/update_suite.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index ab68cd0f0d..0e90bbf828 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -246,7 +246,7 @@ def compare_warnings check.call warnings[idx] != "race" when "nodeadlock" check.call warnings[idx] != "deadlock" - when "nocrash", "fixpoint", "notimeout", "cram" + when "nocrash", "fixpoint", "notimeout", "cram", "nocheck" check.call true end end @@ -334,6 +334,8 @@ def parse_tests (lines) tests[-42] = "notimeout" elsif obj =~ /CRAM/ then tests[-42] = "cram" + elsif obj =~ /NOCHECK/ then + tests[-42] = "nocheck" end next if obj =~ /^\s*\/\// || obj =~ /^\s*\/\*([^*]|\*+[^*\/])*\*\/$/ todo << i if obj =~ /TODO|SKIP/ From 5946abce0cb80d1d8f245da34c471f3f38d87b23 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Jul 2024 18:40:00 +0300 Subject: [PATCH 292/566] Add NOCHECK and another annotations --- tests/regression/00-sanity/37-long-double.c | 1 + .../52-thread-unsafe-libfuns-single-thread.c | 1 + tests/regression/01-cpa/51-marshal.c | 1 + tests/regression/02-base/57-meet-def-exc.c | 1 + .../regression/03-practical/03-pthread_ptrs.c | 16 +- .../03-practical/22-nested-infinite-loops.c | 1 + .../03-practical/35-base-mutex-macos.c | 2 +- .../10-synch/07-thread_self_create.c | 2 +- .../10-synch/27-tid-array-malloc-2.c | 5 +- .../13-privatized/62-global-threadid.c | 2 +- .../65-threadreturn-cpa-remove.c | 2 +- .../20-slr_term/01-no-int-context.c | 2 +- tests/regression/20-slr_term/02-global-inc.c | 2 +- tests/regression/20-slr_term/03-3ctxs.c | 2 +- tests/regression/20-slr_term/05-selfloop.c | 1 + .../22-partitioned_arrays/08-unsupported.c | 1 + .../12-was_problematic_2.c | 1 + .../13-was_problematic_3.c | 1 + .../22-partitioned_arrays/16-refine-meet.c | 1 + .../22-partitioned_arrays/19-fixpoint.c | 1 + .../22-partitioned_arrays/20-more-fixpoint.c | 1 + .../08-unsupported.c | 1 + .../12-was_problematic_2.c | 1 + .../24-octagon/03-previously_problematic_a.c | 2 +- .../24-octagon/04-previously_problematic_b.c | 2 +- .../24-octagon/05-previously_problematic_c.c | 2 +- .../24-octagon/06-previously_problematic_d.c | 2 +- .../24-octagon/07-previously_problematic_e.c | 2 +- .../24-octagon/08-previously_problematic_f.c | 2 +- .../24-octagon/09-previously_problematic_g.c | 2 +- .../24-octagon/10-previously_problematic_h.c | 2 +- .../24-octagon/11-previously_problematic_i.c | 2 +- .../24-octagon/12-previously_problematic_j.c | 1 + tests/regression/25-vla/03-calls.c | 1 + .../02-bot-during-condition.c | 1 + .../27-inv_invariants/07-more-bot.c | 1 + tests/regression/28-race_reach/95-deref-fun.c | 2 +- .../regression/28-race_reach/96-deref-fun2.c | 2 +- .../29-svcomp/25-writing-into-char-array.c | 1 + tests/regression/29-svcomp/26-ikinds_if.c | 1 + tests/regression/29-svcomp/29-witness.c | 1 + tests/regression/29-svcomp/32-no-ov.c | 2 +- tests/regression/33-constants/01-const.c | 1 + tests/regression/33-constants/02-simple.c | 2 +- .../33-constants/03-empty-not-dead-branch.c | 2 +- .../33-constants/04-empty-not-dead.c | 2 +- .../regression/33-constants/05-fun_ptranal.c | 1 + .../34-localwn_restart/05-nested.w.counter.c | 1 + .../65-multi-lock-producer-consumer.c | 1 + .../38-int-refinements/05-invalid-widen.c | 1 + tests/regression/41-stdlib/03-noqsort.c | 1 + tests/regression/41-stdlib/03-noqsort.t | 2 +- tests/regression/43-struct-domain/12-aget.c | 2 +- .../43-struct-domain/25-aget-keyed.c | 2 +- tests/regression/44-trier_analyzer/00-A0.c | 1 + tests/regression/44-trier_analyzer/01-A1.c | 1 + tests/regression/44-trier_analyzer/02-abc.c | 1 + tests/regression/44-trier_analyzer/03-break.c | 1 + tests/regression/44-trier_analyzer/13-ifif.c | 1 + tests/regression/44-trier_analyzer/15-P1.c | 1 + tests/regression/44-trier_analyzer/16-P2.c | 1 + tests/regression/44-trier_analyzer/17-P3.c | 1 + tests/regression/44-trier_analyzer/23-rec0.c | 1 + tests/regression/44-trier_analyzer/33-recA.c | 1 + .../46-apron2/28-sv-comp-unroll-term.c | 2 +- .../34-iset_previously_problematic_c.c | 1 + .../36-iset_previosuly_problematic_g.c | 1 + .../37-iset_previosuly_problematic_f.c | 1 + .../39-iset_previosuly_problematic_d.c | 1 + .../45-iset_previosuly_problematic_i.c | 1 + .../47-iset_previously_problematic_a.c | 1 + .../49-iset_previously_problematic_b.c | 1 + .../51-iset_previosuly_problematic_e.c | 1 + .../53-iset_previously_problematic_h.c | 1 + tests/regression/46-apron2/58-issue-1249.c | 2 +- tests/regression/46-apron2/59-issue-1319.c | 4 +- tests/regression/46-apron2/60-issue-1338.c | 1 + .../46-apron2/82-fixpoint-not-reached.c | 2 +- .../19-mukherjee_fig_3_11.c | 2 +- tests/regression/55-loop-unrolling/08-bad.c | 1 + tests/regression/55-loop-unrolling/08-bad.t | 4 +- .../11-unrolled-loop-invariant.c | 1 + .../11-unrolled-loop-invariant.t | 206 +++++++++--------- .../56-witness/46-top-bool-invariant.c | 2 +- .../56-witness/47-top-int-invariant.c | 2 +- .../56-witness/52-witness-lifter-ps2.c | 1 + .../56-witness/53-witness-lifter-ps3.c | 1 + .../61-evalAssert/01-union_evalAssert.c | 2 +- tests/regression/63-affeq/14-norm_inv.c | 1 + tests/regression/63-affeq/17-verify.c | 2 +- .../00-was_problematic_2.c | 1 + .../03-was_problematic_3.c | 1 + .../66-interval-set-one/14-no-int-context.c | 2 +- .../regression/66-interval-set-one/39-calls.c | 1 + .../regression/66-interval-set-one/93-enum.c | 1 + .../03-def_exc-interval-inconsistent.c | 1 + .../67-interval-sets-two/04-unsupported.c | 1 + .../67-interval-sets-two/14-trylock_rc_slr.c | 1 + .../67-interval-sets-two/15-interval-bot.c | 2 +- .../67-interval-sets-two/24-arithmetic-bot.c | 1 + .../67-interval-sets-two/31-ptrdiff.c | 1 + .../67-interval-sets-two/44-comparision-bot.c | 1 + .../56-interval-set-dead-code.c | 1 + .../58-interval-set-dead-code-with-fun-call.c | 1 + .../71-doublelocking/15-rec-dyn-nested.c | 2 +- .../15-juliet-uaf-global-var.c | 1 + 106 files changed, 219 insertions(+), 153 deletions(-) diff --git a/tests/regression/00-sanity/37-long-double.c b/tests/regression/00-sanity/37-long-double.c index 01c9b8bb9b..b4eb5e29c8 100644 --- a/tests/regression/00-sanity/37-long-double.c +++ b/tests/regression/00-sanity/37-long-double.c @@ -1,3 +1,4 @@ +// CRAM int main() { long double l = 0.0L; return (int)l; diff --git a/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c index 94c0f3efeb..aa1bc24aa4 100644 --- a/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c +++ b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c @@ -1,4 +1,5 @@ // PARAM: --enable allglobs --set ana.activated[+] threadJoins +// CRAM #include #include diff --git a/tests/regression/01-cpa/51-marshal.c b/tests/regression/01-cpa/51-marshal.c index bf4e8faebf..c718159e33 100644 --- a/tests/regression/01-cpa/51-marshal.c +++ b/tests/regression/01-cpa/51-marshal.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval +// NOCHECK void callee(int j) { j++; } diff --git a/tests/regression/02-base/57-meet-def-exc.c b/tests/regression/02-base/57-meet-def-exc.c index d66b1120d6..cf24431f8c 100644 --- a/tests/regression/02-base/57-meet-def-exc.c +++ b/tests/regression/02-base/57-meet-def-exc.c @@ -1,3 +1,4 @@ +// NOCHECK void main(void) { int x; int i = 41; diff --git a/tests/regression/03-practical/03-pthread_ptrs.c b/tests/regression/03-practical/03-pthread_ptrs.c index c619c994b2..0694698af8 100644 --- a/tests/regression/03-practical/03-pthread_ptrs.c +++ b/tests/regression/03-practical/03-pthread_ptrs.c @@ -1,5 +1,5 @@ #include - +// NOCHECK static pthread_t sid1 ; static pthread_t sid2 ; static pthread_t sid3 ; @@ -15,22 +15,22 @@ void *fn2(void) { extern void *fn3(void * a); -int main() { +int main() { /* normal call to fn1 */ pthread_create(&sid1, NULL, fn1, NULL); - - /* ignore parameter (cast parameter to void) call */ + + /* ignore parameter (cast parameter to void) call */ pthread_create(&sid2, NULL, (void *(*)(void * )) (& fn2), NULL); - + /* we create a unknown thread -- that can't be good */ pthread_create(&sid3, NULL, &fn3, NULL); pthread_join(sid3, NULL); - + pthread_join(sid2, NULL); - + pthread_join(sid1, NULL); - + return 0; } diff --git a/tests/regression/03-practical/22-nested-infinite-loops.c b/tests/regression/03-practical/22-nested-infinite-loops.c index f3e1fed23b..658bcbacb0 100644 --- a/tests/regression/03-practical/22-nested-infinite-loops.c +++ b/tests/regression/03-practical/22-nested-infinite-loops.c @@ -1,4 +1,5 @@ // https://github.com/goblint/analyzer/issues/231#issuecomment-868369123 +// NOCHECK: should check CFG? int main(void) { int x = 0; while(1) { diff --git a/tests/regression/03-practical/35-base-mutex-macos.c b/tests/regression/03-practical/35-base-mutex-macos.c index 4f4dc417fc..a83022e91b 100644 --- a/tests/regression/03-practical/35-base-mutex-macos.c +++ b/tests/regression/03-practical/35-base-mutex-macos.c @@ -1,5 +1,5 @@ // Intentionally no #include , because we want to imitate/debug MacOS construction on anything. - +// CRAM #define __PTHREAD_MUTEX_SIZE__ 56 struct _opaque_pthread_mutex_t { diff --git a/tests/regression/10-synch/07-thread_self_create.c b/tests/regression/10-synch/07-thread_self_create.c index 473a26a25b..18c5f3ee83 100644 --- a/tests/regression/10-synch/07-thread_self_create.c +++ b/tests/regression/10-synch/07-thread_self_create.c @@ -1,5 +1,5 @@ // PARAM: --set ana.activated[+] thread -// Checks termination of thread analysis with a thread who is its own single parent. +// NOTIMEOUT: Checks termination of thread analysis with a thread who is its own single parent. #include void *t_fun(void *arg) { diff --git a/tests/regression/10-synch/27-tid-array-malloc-2.c b/tests/regression/10-synch/27-tid-array-malloc-2.c index 462901459a..d1b41843a7 100644 --- a/tests/regression/10-synch/27-tid-array-malloc-2.c +++ b/tests/regression/10-synch/27-tid-array-malloc-2.c @@ -1,4 +1,5 @@ -// PARAM: --set ana.activated[+] thread +// PARAM: --set ana.activated[+] thread +// NOCRASH #include #include @@ -19,7 +20,7 @@ int main() pthread_create(&t[tid], 0, thread, 0); tid++; pthread_create(&t[tid], 0, thread, 0); - + tid=0; pthread_join(t[tid], 0); tid++; diff --git a/tests/regression/13-privatized/62-global-threadid.c b/tests/regression/13-privatized/62-global-threadid.c index 38d21700ea..b5f3c831bb 100644 --- a/tests/regression/13-privatized/62-global-threadid.c +++ b/tests/regression/13-privatized/62-global-threadid.c @@ -1,5 +1,5 @@ #include - +// NOCHECK pthread_t id; extern void magic(); diff --git a/tests/regression/13-privatized/65-threadreturn-cpa-remove.c b/tests/regression/13-privatized/65-threadreturn-cpa-remove.c index 3bbe85b3ea..58d8c9ea2f 100644 --- a/tests/regression/13-privatized/65-threadreturn-cpa-remove.c +++ b/tests/regression/13-privatized/65-threadreturn-cpa-remove.c @@ -1,5 +1,5 @@ #include - +// NOCHECK int d; pthread_t g; enum { b } c() {} diff --git a/tests/regression/20-slr_term/01-no-int-context.c b/tests/regression/20-slr_term/01-no-int-context.c index d057098559..ce8d3cb833 100644 --- a/tests/regression/20-slr_term/01-no-int-context.c +++ b/tests/regression/20-slr_term/01-no-int-context.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.interval --set solver slr3t --disable ana.base.context.int - +// NOCHECK int f (int i) { // -2 return i+1; } // -3 void g(int j) { // -4 diff --git a/tests/regression/20-slr_term/02-global-inc.c b/tests/regression/20-slr_term/02-global-inc.c index cfb2432999..fc2f9100df 100644 --- a/tests/regression/20-slr_term/02-global-inc.c +++ b/tests/regression/20-slr_term/02-global-inc.c @@ -1,4 +1,4 @@ - +// NOCHECK int g = 0; int f = 0; diff --git a/tests/regression/20-slr_term/03-3ctxs.c b/tests/regression/20-slr_term/03-3ctxs.c index d0236960ea..b622549419 100644 --- a/tests/regression/20-slr_term/03-3ctxs.c +++ b/tests/regression/20-slr_term/03-3ctxs.c @@ -1,4 +1,4 @@ - +// NOCHECK int f(int j){ return j+1; } diff --git a/tests/regression/20-slr_term/05-selfloop.c b/tests/regression/20-slr_term/05-selfloop.c index 94286393a6..638d11cb1b 100644 --- a/tests/regression/20-slr_term/05-selfloop.c +++ b/tests/regression/20-slr_term/05-selfloop.c @@ -1,3 +1,4 @@ +// NOCHECK void f() { } void g() { } void h() { } diff --git a/tests/regression/22-partitioned_arrays/08-unsupported.c b/tests/regression/22-partitioned_arrays/08-unsupported.c index 51e897d840..73f5871d9c 100644 --- a/tests/regression/22-partitioned_arrays/08-unsupported.c +++ b/tests/regression/22-partitioned_arrays/08-unsupported.c @@ -1,6 +1,7 @@ // PARAM: --enable ana.int.interval --disable exp.fast_global_inits --set ana.base.arrays.domain partitioned // This is just to test that the analysis does not cause problems for features that are not explicitly dealt with +// NOCHECK: what problems? int main(void) { callok(); } diff --git a/tests/regression/22-partitioned_arrays/12-was_problematic_2.c b/tests/regression/22-partitioned_arrays/12-was_problematic_2.c index fd57549008..2ff396feb4 100644 --- a/tests/regression/22-partitioned_arrays/12-was_problematic_2.c +++ b/tests/regression/22-partitioned_arrays/12-was_problematic_2.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned +// NOCHECK int main(void) { int arr[260]; diff --git a/tests/regression/22-partitioned_arrays/13-was_problematic_3.c b/tests/regression/22-partitioned_arrays/13-was_problematic_3.c index ff7e839753..1822af4f0a 100644 --- a/tests/regression/22-partitioned_arrays/13-was_problematic_3.c +++ b/tests/regression/22-partitioned_arrays/13-was_problematic_3.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned +// NOCHECK struct some_struct { int dir[7]; diff --git a/tests/regression/22-partitioned_arrays/16-refine-meet.c b/tests/regression/22-partitioned_arrays/16-refine-meet.c index 05a73a70ba..7f238ab858 100644 --- a/tests/regression/22-partitioned_arrays/16-refine-meet.c +++ b/tests/regression/22-partitioned_arrays/16-refine-meet.c @@ -1,4 +1,5 @@ // PARAM: --set ana.base.arrays.domain partitioned +// NOCHECK int garr[7]; int main(int argc, char **argv) diff --git a/tests/regression/22-partitioned_arrays/19-fixpoint.c b/tests/regression/22-partitioned_arrays/19-fixpoint.c index 1ac55216dd..30d7821db5 100644 --- a/tests/regression/22-partitioned_arrays/19-fixpoint.c +++ b/tests/regression/22-partitioned_arrays/19-fixpoint.c @@ -1,4 +1,5 @@ // PARAM: --set ana.base.arrays.domain partitioned +// FIXPOINT #include int stored_elements[20]; diff --git a/tests/regression/22-partitioned_arrays/20-more-fixpoint.c b/tests/regression/22-partitioned_arrays/20-more-fixpoint.c index 8181cf85dd..ab87e1944a 100644 --- a/tests/regression/22-partitioned_arrays/20-more-fixpoint.c +++ b/tests/regression/22-partitioned_arrays/20-more-fixpoint.c @@ -1,4 +1,5 @@ // PARAM: --set ana.base.arrays.domain partitioned +// FIXPOINT #include #include #include diff --git a/tests/regression/23-partitioned_arrays_last/08-unsupported.c b/tests/regression/23-partitioned_arrays_last/08-unsupported.c index 0b0a1baca9..cff075d786 100644 --- a/tests/regression/23-partitioned_arrays_last/08-unsupported.c +++ b/tests/regression/23-partitioned_arrays_last/08-unsupported.c @@ -1,6 +1,7 @@ // PARAM: --enable ana.int.interval --disable exp.fast_global_inits --set ana.base.arrays.domain partitioned --set ana.base.partition-arrays.keep-expr "last" // This is just to test that the analysis does not cause problems for features that are not explicitly dealt with +// NOCHECK: what problems? int main(void) { vla(); callok(); diff --git a/tests/regression/23-partitioned_arrays_last/12-was_problematic_2.c b/tests/regression/23-partitioned_arrays_last/12-was_problematic_2.c index 9523510472..d5593ffe8b 100644 --- a/tests/regression/23-partitioned_arrays_last/12-was_problematic_2.c +++ b/tests/regression/23-partitioned_arrays_last/12-was_problematic_2.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.base.partition-arrays.keep-expr "last" +// NOCHECK int main(void) { int arr[260]; diff --git a/tests/regression/24-octagon/03-previously_problematic_a.c b/tests/regression/24-octagon/03-previously_problematic_a.c index 986e2821fd..a266358408 100644 --- a/tests/regression/24-octagon/03-previously_problematic_a.c +++ b/tests/regression/24-octagon/03-previously_problematic_a.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/04-previously_problematic_b.c b/tests/regression/24-octagon/04-previously_problematic_b.c index 62649e925d..e9a239f66b 100644 --- a/tests/regression/24-octagon/04-previously_problematic_b.c +++ b/tests/regression/24-octagon/04-previously_problematic_b.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included typedef int wchar_t; typedef unsigned long size_t; diff --git a/tests/regression/24-octagon/05-previously_problematic_c.c b/tests/regression/24-octagon/05-previously_problematic_c.c index c260646c23..8d08c343ad 100644 --- a/tests/regression/24-octagon/05-previously_problematic_c.c +++ b/tests/regression/24-octagon/05-previously_problematic_c.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/06-previously_problematic_d.c b/tests/regression/24-octagon/06-previously_problematic_d.c index 733b07d015..cca02f523b 100644 --- a/tests/regression/24-octagon/06-previously_problematic_d.c +++ b/tests/regression/24-octagon/06-previously_problematic_d.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/07-previously_problematic_e.c b/tests/regression/24-octagon/07-previously_problematic_e.c index cb9b12cafe..0dee19eb94 100644 --- a/tests/regression/24-octagon/07-previously_problematic_e.c +++ b/tests/regression/24-octagon/07-previously_problematic_e.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/08-previously_problematic_f.c b/tests/regression/24-octagon/08-previously_problematic_f.c index 431b8a6c57..93c4d3195f 100644 --- a/tests/regression/24-octagon/08-previously_problematic_f.c +++ b/tests/regression/24-octagon/08-previously_problematic_f.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/09-previously_problematic_g.c b/tests/regression/24-octagon/09-previously_problematic_g.c index c48184d4dd..910a28703d 100644 --- a/tests/regression/24-octagon/09-previously_problematic_g.c +++ b/tests/regression/24-octagon/09-previously_problematic_g.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/10-previously_problematic_h.c b/tests/regression/24-octagon/10-previously_problematic_h.c index 969ae965df..ff29145789 100644 --- a/tests/regression/24-octagon/10-previously_problematic_h.c +++ b/tests/regression/24-octagon/10-previously_problematic_h.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/11-previously_problematic_i.c b/tests/regression/24-octagon/11-previously_problematic_i.c index f1956919d8..517b8e58d2 100644 --- a/tests/regression/24-octagon/11-previously_problematic_i.c +++ b/tests/regression/24-octagon/11-previously_problematic_i.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included char buf2[67]; diff --git a/tests/regression/24-octagon/12-previously_problematic_j.c b/tests/regression/24-octagon/12-previously_problematic_j.c index 208a89d50f..a289e70de3 100644 --- a/tests/regression/24-octagon/12-previously_problematic_j.c +++ b/tests/regression/24-octagon/12-previously_problematic_j.c @@ -1,4 +1,5 @@ // SKIP PARAM: --set ana.activated[+] apron +// NOCHECK void main(void) { int i = 0; int j = i; diff --git a/tests/regression/25-vla/03-calls.c b/tests/regression/25-vla/03-calls.c index 133e89f704..fbe38af248 100644 --- a/tests/regression/25-vla/03-calls.c +++ b/tests/regression/25-vla/03-calls.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval --disable ana.int.def_exc --set ana.base.arrays.domain partitioned // Variable-sized arrays +// NOCHECK void foo(int n, int a[n]); void foo2(int n, int a[30][n]); void foo3(int n, int a[n][30]); diff --git a/tests/regression/27-inv_invariants/02-bot-during-condition.c b/tests/regression/27-inv_invariants/02-bot-during-condition.c index a94c65bb81..88c5ff2ada 100644 --- a/tests/regression/27-inv_invariants/02-bot-during-condition.c +++ b/tests/regression/27-inv_invariants/02-bot-during-condition.c @@ -1,3 +1,4 @@ +// NOCHECK int main () { int tmp; diff --git a/tests/regression/27-inv_invariants/07-more-bot.c b/tests/regression/27-inv_invariants/07-more-bot.c index e4b677b599..8e5819158e 100644 --- a/tests/regression/27-inv_invariants/07-more-bot.c +++ b/tests/regression/27-inv_invariants/07-more-bot.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval // Adapted from sv-comp array-programs/partial_mod_count_1.c +// NOCHECK int N = 1000; int main(){ int i; diff --git a/tests/regression/28-race_reach/95-deref-fun.c b/tests/regression/28-race_reach/95-deref-fun.c index 04cf1a8723..f1431d9221 100644 --- a/tests/regression/28-race_reach/95-deref-fun.c +++ b/tests/regression/28-race_reach/95-deref-fun.c @@ -1,6 +1,6 @@ // PARAM: --enable ana.sv-comp.enabled --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" #include - +// NOCRASH void foo(int (*callback)()) { } diff --git a/tests/regression/28-race_reach/96-deref-fun2.c b/tests/regression/28-race_reach/96-deref-fun2.c index 9e76c8604d..cb3aee7535 100644 --- a/tests/regression/28-race_reach/96-deref-fun2.c +++ b/tests/regression/28-race_reach/96-deref-fun2.c @@ -1,6 +1,6 @@ // PARAM: --enable ana.sv-comp.enabled --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" #include - +// NOCRASH #define SVCOMP 1 #include diff --git a/tests/regression/29-svcomp/25-writing-into-char-array.c b/tests/regression/29-svcomp/25-writing-into-char-array.c index 8894007d23..ce8fea40f4 100644 --- a/tests/regression/29-svcomp/25-writing-into-char-array.c +++ b/tests/regression/29-svcomp/25-writing-into-char-array.c @@ -1,3 +1,4 @@ +// NOCHECK #include struct __anonstruct_mbox_t_232 { int one; diff --git a/tests/regression/29-svcomp/26-ikinds_if.c b/tests/regression/29-svcomp/26-ikinds_if.c index b902659f47..a8f726353f 100644 --- a/tests/regression/29-svcomp/26-ikinds_if.c +++ b/tests/regression/29-svcomp/26-ikinds_if.c @@ -1,4 +1,5 @@ //PARAM: --enable ana.int.interval +// NOCHECK #include static long sound_ioctl(unsigned int cmd , unsigned long arg ) diff --git a/tests/regression/29-svcomp/29-witness.c b/tests/regression/29-svcomp/29-witness.c index 72d1c4435a..b56b47a74c 100644 --- a/tests/regression/29-svcomp/29-witness.c +++ b/tests/regression/29-svcomp/29-witness.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.sv-comp.enabled --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" +// NOCRASH #include #include diff --git a/tests/regression/29-svcomp/32-no-ov.c b/tests/regression/29-svcomp/32-no-ov.c index 0167098c29..3612b34459 100644 --- a/tests/regression/29-svcomp/32-no-ov.c +++ b/tests/regression/29-svcomp/32-no-ov.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.interval --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" - +// CRAM int main(){ // This is not an overflow, just implementation defined behavior on a cast int data = ((int)(rand() & 1 ? (((unsigned)rand()<<30) ^ ((unsigned)rand()<<15) ^ rand()) : -(((unsigned)rand()<<30) ^ ((unsigned)rand()<<15) ^ rand()) - 1)); diff --git a/tests/regression/33-constants/01-const.c b/tests/regression/33-constants/01-const.c index 8db984a4b3..436a389545 100644 --- a/tests/regression/33-constants/01-const.c +++ b/tests/regression/33-constants/01-const.c @@ -1,5 +1,6 @@ //PARAM: --set ana.activated '["constants"]' // intentional explicit ana.activated to do tutorial in isolation +// NOCHECK int f(int a, int b){ int d = 3; int z = a + d; diff --git a/tests/regression/33-constants/02-simple.c b/tests/regression/33-constants/02-simple.c index aa370aa4da..75cd33ee8a 100644 --- a/tests/regression/33-constants/02-simple.c +++ b/tests/regression/33-constants/02-simple.c @@ -1,6 +1,6 @@ //PARAM: --set ana.activated '["constants"]' // intentional explicit ana.activated to do tutorial in isolation - +// NOCHECK int main(){ int x = 3; int y = 4; diff --git a/tests/regression/33-constants/03-empty-not-dead-branch.c b/tests/regression/33-constants/03-empty-not-dead-branch.c index 2b6d662589..2bd6a10190 100644 --- a/tests/regression/33-constants/03-empty-not-dead-branch.c +++ b/tests/regression/33-constants/03-empty-not-dead-branch.c @@ -1,6 +1,6 @@ //PARAM: --set ana.activated '["constants"]' // intentional explicit ana.activated to do tutorial in isolation - +// NOCHECK int g; int main() { diff --git a/tests/regression/33-constants/04-empty-not-dead.c b/tests/regression/33-constants/04-empty-not-dead.c index 8ad4199aa8..377a066370 100644 --- a/tests/regression/33-constants/04-empty-not-dead.c +++ b/tests/regression/33-constants/04-empty-not-dead.c @@ -1,6 +1,6 @@ //PARAM: --set ana.activated '["constants"]' // intentional explicit ana.activated to do tutorial in isolation - +// NOCHECK int g; int main() { diff --git a/tests/regression/33-constants/05-fun_ptranal.c b/tests/regression/33-constants/05-fun_ptranal.c index 5ebaf24e22..bb8f2146e5 100644 --- a/tests/regression/33-constants/05-fun_ptranal.c +++ b/tests/regression/33-constants/05-fun_ptranal.c @@ -1,5 +1,6 @@ //PARAM: --set ana.activated '["constants", "ptranal"]' // intentional explicit ana.activated to do tutorial in isolation +// NOCHECK int f(int a, int b){ int d = 3; int z = a + d; diff --git a/tests/regression/34-localwn_restart/05-nested.w.counter.c b/tests/regression/34-localwn_restart/05-nested.w.counter.c index 2d0cca1853..7cc7c6ce59 100644 --- a/tests/regression/34-localwn_restart/05-nested.w.counter.c +++ b/tests/regression/34-localwn_restart/05-nested.w.counter.c @@ -1,4 +1,5 @@ // Variant of nested.c with a counter. +// NOCHECK void main() { int z = 0; diff --git a/tests/regression/36-apron/65-multi-lock-producer-consumer.c b/tests/regression/36-apron/65-multi-lock-producer-consumer.c index ae71c36dcf..e7041a09d3 100644 --- a/tests/regression/36-apron/65-multi-lock-producer-consumer.c +++ b/tests/regression/36-apron/65-multi-lock-producer-consumer.c @@ -1,5 +1,6 @@ // SKIP PARAM: --set ana.activated[+] apron --enable ana.sv-comp.functions --set ana.relation.privatization mutex-meet --set ana.apron.domain polyhedra // TODO: why doesn't mutex-meet-tid succeed? a widening loses some upper bound and we forget a possible overflow, succeeds with assume_none +// NOCHECK #include #include diff --git a/tests/regression/38-int-refinements/05-invalid-widen.c b/tests/regression/38-int-refinements/05-invalid-widen.c index 59d76019a8..ad39ff761c 100644 --- a/tests/regression/38-int-refinements/05-invalid-widen.c +++ b/tests/regression/38-int-refinements/05-invalid-widen.c @@ -1,4 +1,5 @@ //PARAM: --set ana.int.refinement once --enable ana.int.enums +// NOCRASH #include int main() { diff --git a/tests/regression/41-stdlib/03-noqsort.c b/tests/regression/41-stdlib/03-noqsort.c index e7a1de89a3..22672083d7 100644 --- a/tests/regression/41-stdlib/03-noqsort.c +++ b/tests/regression/41-stdlib/03-noqsort.c @@ -1,4 +1,5 @@ // PARAM: --set pre.cppflags[+] -DGOBLINT_NO_QSORT +// CRAM #include // There should be no CIL warning about multiple definitions here diff --git a/tests/regression/41-stdlib/03-noqsort.t b/tests/regression/41-stdlib/03-noqsort.t index f8082a768c..cd56e0047d 100644 --- a/tests/regression/41-stdlib/03-noqsort.t +++ b/tests/regression/41-stdlib/03-noqsort.t @@ -1,7 +1,7 @@ There should be no CIL warning about multiple definitions: $ goblint --set pre.cppflags[+] -DGOBLINT_NO_QSORT 03-noqsort.c - [Warning][Deadcode] Function 'qsort' is uncalled: 1 LLoC (03-noqsort.c:5:1-6:1) + [Warning][Deadcode] Function 'qsort' is uncalled: 1 LLoC (03-noqsort.c:6:1-7:1) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 2 dead: 1 (1 in uncalled functions) diff --git a/tests/regression/43-struct-domain/12-aget.c b/tests/regression/43-struct-domain/12-aget.c index 193e463418..6e67cefe75 100644 --- a/tests/regression/43-struct-domain/12-aget.c +++ b/tests/regression/43-struct-domain/12-aget.c @@ -1,5 +1,5 @@ // PARAM: --set ana.base.structs.domain "sets" - +// NOCHECK // This is shortened from aget.c in the bench repository // There was an issue when testing arrays, as they init to bottom and this code triggers narrowing. /* Generated by CIL v. 1.3.6 */ diff --git a/tests/regression/43-struct-domain/25-aget-keyed.c b/tests/regression/43-struct-domain/25-aget-keyed.c index 94d5495ae1..09c80c44cc 100644 --- a/tests/regression/43-struct-domain/25-aget-keyed.c +++ b/tests/regression/43-struct-domain/25-aget-keyed.c @@ -1,5 +1,5 @@ // PARAM: --set ana.base.structs.domain "keyed" - +// NOCHECK // This is shortened from aget.c in the bench repository // There was an issue when testing arrays, as they init to bottom and this code triggers narrowing. /* Generated by CIL v. 1.3.6 */ diff --git a/tests/regression/44-trier_analyzer/00-A0.c b/tests/regression/44-trier_analyzer/00-A0.c index defd7022f0..bfeb7c5509 100644 --- a/tests/regression/44-trier_analyzer/00-A0.c +++ b/tests/regression/44-trier_analyzer/00-A0.c @@ -1,3 +1,4 @@ +// NOCHECK extern int printf(char *, ...); main () { diff --git a/tests/regression/44-trier_analyzer/01-A1.c b/tests/regression/44-trier_analyzer/01-A1.c index a04fafc238..967b8d79d0 100644 --- a/tests/regression/44-trier_analyzer/01-A1.c +++ b/tests/regression/44-trier_analyzer/01-A1.c @@ -1,3 +1,4 @@ +// NOCHECK extern int printf (char *, ...); extern int scanf (char *, ...); diff --git a/tests/regression/44-trier_analyzer/02-abc.c b/tests/regression/44-trier_analyzer/02-abc.c index 6a23697a7b..f29d3787e7 100644 --- a/tests/regression/44-trier_analyzer/02-abc.c +++ b/tests/regression/44-trier_analyzer/02-abc.c @@ -1,3 +1,4 @@ +// NOCHECK #include #include diff --git a/tests/regression/44-trier_analyzer/03-break.c b/tests/regression/44-trier_analyzer/03-break.c index 4c87a8277f..78a8cf26d6 100644 --- a/tests/regression/44-trier_analyzer/03-break.c +++ b/tests/regression/44-trier_analyzer/03-break.c @@ -1,3 +1,4 @@ +// NOCHECK extern int printf(); main () { diff --git a/tests/regression/44-trier_analyzer/13-ifif.c b/tests/regression/44-trier_analyzer/13-ifif.c index 3e456a9576..e9025f1fa8 100644 --- a/tests/regression/44-trier_analyzer/13-ifif.c +++ b/tests/regression/44-trier_analyzer/13-ifif.c @@ -1,3 +1,4 @@ +// NOCHECK extern int printf(); extern int scanf(); diff --git a/tests/regression/44-trier_analyzer/15-P1.c b/tests/regression/44-trier_analyzer/15-P1.c index 2690a6da44..b169ea4876 100644 --- a/tests/regression/44-trier_analyzer/15-P1.c +++ b/tests/regression/44-trier_analyzer/15-P1.c @@ -1,3 +1,4 @@ +// NOCHECK main () { int y, a; int *p, *q, *t; diff --git a/tests/regression/44-trier_analyzer/16-P2.c b/tests/regression/44-trier_analyzer/16-P2.c index 3942760be7..2f27d5dbe0 100644 --- a/tests/regression/44-trier_analyzer/16-P2.c +++ b/tests/regression/44-trier_analyzer/16-P2.c @@ -1,3 +1,4 @@ +// NOCHECK main () { int y, a, b; int *p, *q; diff --git a/tests/regression/44-trier_analyzer/17-P3.c b/tests/regression/44-trier_analyzer/17-P3.c index 7b8c8a13d8..0bbc9ee2d2 100644 --- a/tests/regression/44-trier_analyzer/17-P3.c +++ b/tests/regression/44-trier_analyzer/17-P3.c @@ -1,3 +1,4 @@ +// NOCHECK #include extern void *malloc(size_t); diff --git a/tests/regression/44-trier_analyzer/23-rec0.c b/tests/regression/44-trier_analyzer/23-rec0.c index aa13bbac8e..ee43820bb1 100644 --- a/tests/regression/44-trier_analyzer/23-rec0.c +++ b/tests/regression/44-trier_analyzer/23-rec0.c @@ -1,3 +1,4 @@ +// NOCHECK extern int printf(char *, ...); extern int scanf(char *, ...); extern void exit(int); diff --git a/tests/regression/44-trier_analyzer/33-recA.c b/tests/regression/44-trier_analyzer/33-recA.c index 74fa54b768..2938403326 100644 --- a/tests/regression/44-trier_analyzer/33-recA.c +++ b/tests/regression/44-trier_analyzer/33-recA.c @@ -1,4 +1,5 @@ //PARAM: --enable ana.context.widen +// NOCHECK //Needs context widening even with only def_exc extern int scanf(char *, ...); extern int printf(char *, ...); diff --git a/tests/regression/46-apron2/28-sv-comp-unroll-term.c b/tests/regression/46-apron2/28-sv-comp-unroll-term.c index a22b0e16bd..2a1f947dca 100644 --- a/tests/regression/46-apron2/28-sv-comp-unroll-term.c +++ b/tests/regression/46-apron2/28-sv-comp-unroll-term.c @@ -2,7 +2,7 @@ // Minimized from sv-benchmarks/c/ldv-linux-3.4-simple/32_1_cilled_ok_nondet_linux-3.4-32_1-drivers--staging--speakup--speakup_spkout.ko-ldv_main0_sequence_infinite_withcheck_stateful.cil.out.i // using loop unrolling of 1. -// Used to not terminate. +// NOTIMEOUT: Used to not terminate. struct speakup_info_t { int port_tts; diff --git a/tests/regression/46-apron2/34-iset_previously_problematic_c.c b/tests/regression/46-apron2/34-iset_previously_problematic_c.c index 3c1847bbcf..2b0f526957 100644 --- a/tests/regression/46-apron2/34-iset_previously_problematic_c.c +++ b/tests/regression/46-apron2/34-iset_previously_problematic_c.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int i = 0; diff --git a/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c b/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c index 4cbcaef064..89253052d5 100644 --- a/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c +++ b/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int i = 0; diff --git a/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c b/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c index 918e67b0e8..c88aabe377 100644 --- a/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c +++ b/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int a[256]; diff --git a/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c b/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c index 18a53d31aa..480691409d 100644 --- a/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c +++ b/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int l; diff --git a/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c b/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c index dab6078b98..b9506cb453 100644 --- a/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c +++ b/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK char buf2[67]; int main(int argc, char **argv) diff --git a/tests/regression/46-apron2/47-iset_previously_problematic_a.c b/tests/regression/46-apron2/47-iset_previously_problematic_a.c index 2bf6044560..15d61d436b 100644 --- a/tests/regression/46-apron2/47-iset_previously_problematic_a.c +++ b/tests/regression/46-apron2/47-iset_previously_problematic_a.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int top; diff --git a/tests/regression/46-apron2/49-iset_previously_problematic_b.c b/tests/regression/46-apron2/49-iset_previously_problematic_b.c index 19d925013c..336efa4eb0 100644 --- a/tests/regression/46-apron2/49-iset_previously_problematic_b.c +++ b/tests/regression/46-apron2/49-iset_previously_problematic_b.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK typedef int wchar_t; typedef unsigned long size_t; diff --git a/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c b/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c index 551bb731e9..582cd4916c 100644 --- a/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c +++ b/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int j = -1; diff --git a/tests/regression/46-apron2/53-iset_previously_problematic_h.c b/tests/regression/46-apron2/53-iset_previously_problematic_h.c index 05489e47dc..242f1fb377 100644 --- a/tests/regression/46-apron2/53-iset_previously_problematic_h.c +++ b/tests/regression/46-apron2/53-iset_previously_problematic_h.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int iter = 0; diff --git a/tests/regression/46-apron2/58-issue-1249.c b/tests/regression/46-apron2/58-issue-1249.c index 862c539d15..903eafdac8 100644 --- a/tests/regression/46-apron2/58-issue-1249.c +++ b/tests/regression/46-apron2/58-issue-1249.c @@ -2,7 +2,7 @@ int *a; int b; void c(int d) { - // *a is a null pointer here, so we should warn but maybe not crash + // NOCRASH: *a is a null pointer here, so we should warn but maybe not crash *a = d; } int main() { diff --git a/tests/regression/46-apron2/59-issue-1319.c b/tests/regression/46-apron2/59-issue-1319.c index 1c11d6093e..62520c95c1 100644 --- a/tests/regression/46-apron2/59-issue-1319.c +++ b/tests/regression/46-apron2/59-issue-1319.c @@ -8,7 +8,7 @@ int main() t = &c; - // Type of *t and c do not match, this caused a crash before + // NOCRASH: Type of *t and c do not match, this caused a crash before if(*t == 'a') { t++; } @@ -18,7 +18,7 @@ int main() int other() { - // Same problem, but a bit more involved + // NOCRASH: Same problem, but a bit more involved unsigned char *t; char buf[100] = "bliblablubapk\r"; diff --git a/tests/regression/46-apron2/60-issue-1338.c b/tests/regression/46-apron2/60-issue-1338.c index 899fe613b3..92afa3b74f 100644 --- a/tests/regression/46-apron2/60-issue-1338.c +++ b/tests/regression/46-apron2/60-issue-1338.c @@ -1,4 +1,5 @@ // SKIP PARAM: --set ana.activated[+] apron +// NOCRASH #include int main() { diff --git a/tests/regression/46-apron2/82-fixpoint-not-reached.c b/tests/regression/46-apron2/82-fixpoint-not-reached.c index 1dff575a7c..66f00770da 100644 --- a/tests/regression/46-apron2/82-fixpoint-not-reached.c +++ b/tests/regression/46-apron2/82-fixpoint-not-reached.c @@ -1,5 +1,5 @@ // SKIP PARAM: --set sem.int.signed_overflow assume_none --set ana.activated[+] apron - +// FIXPOINT int main() { int minInt = -2147483647 + -1; int x = (minInt + -1) +1; diff --git a/tests/regression/52-apron-mukherjee/19-mukherjee_fig_3_11.c b/tests/regression/52-apron-mukherjee/19-mukherjee_fig_3_11.c index b00a2b2270..73499179c5 100644 --- a/tests/regression/52-apron-mukherjee/19-mukherjee_fig_3_11.c +++ b/tests/regression/52-apron-mukherjee/19-mukherjee_fig_3_11.c @@ -1,5 +1,5 @@ // SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.activated[+] threadJoins -// TODO: checks nothing? +// NOCHECK: checks nothing? #include int x; diff --git a/tests/regression/55-loop-unrolling/08-bad.c b/tests/regression/55-loop-unrolling/08-bad.c index e2ad0f0453..df9131d6bf 100644 --- a/tests/regression/55-loop-unrolling/08-bad.c +++ b/tests/regression/55-loop-unrolling/08-bad.c @@ -1,4 +1,5 @@ // PARAM: --set exp.unrolling-factor 1 --enable dbg.run_cil_check +// CRAM int main() { int m; diff --git a/tests/regression/55-loop-unrolling/08-bad.t b/tests/regression/55-loop-unrolling/08-bad.t index 11cded728f..f49ad67b5b 100644 --- a/tests/regression/55-loop-unrolling/08-bad.t +++ b/tests/regression/55-loop-unrolling/08-bad.t @@ -1,6 +1,6 @@ $ goblint --set lib.activated '[]' --set exp.unrolling-factor 1 --enable justcil --set dbg.justcil-printer clean 08-bad.c - [Info] unrolling loop at 08-bad.c:8:7-8:23 with factor 1 - [Info] unrolling loop at 08-bad.c:14:8-14:24 with factor 1 + [Info] unrolling loop at 08-bad.c:9:7-9:23 with factor 1 + [Info] unrolling loop at 08-bad.c:15:8-15:24 with factor 1 int main(void) { int m ; diff --git a/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.c b/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.c index 096c031cf1..4a3f7c1cfa 100644 --- a/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.c +++ b/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.c @@ -1,3 +1,4 @@ +// CRAM int main() { int i = 0; while (i < 10) diff --git a/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t b/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t index 32ec663717..3a3b7c43cf 100644 --- a/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t +++ b/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t @@ -1,7 +1,7 @@ $ cfgDot --unroll 1 11-unrolled-loop-invariant.c - [Info] unrolling loop at 11-unrolled-loop-invariant.c:3:3-4:8 with factor 1 - [Info] unrolling loop at 11-unrolled-loop-invariant.c:8:5-9:10 with factor 1 - [Info] unrolling loop at 11-unrolled-loop-invariant.c:7:3-11:3 with factor 1 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:4:3-5:8 with factor 1 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:9:5-10:10 with factor 1 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:8:3-12:3 with factor 1 $ graph-easy --as=boxart main.dot ┌──────────────────────────────────────────────────────┐ @@ -11,160 +11,160 @@ │ (body) ▼ ┌──────────────────────────────────────────────────────┐ - │ 11-unrolled-loop-invariant.c:2:3-2:12 │ - │ (11-unrolled-loop-invariant.c:2:7-2:12 (synthetic)) │ - │ YAML loc: 11-unrolled-loop-invariant.c:2:3-2:12 │ + │ 11-unrolled-loop-invariant.c:3:3-3:12 │ + │ (11-unrolled-loop-invariant.c:3:7-3:12 (synthetic)) │ + │ YAML loc: 11-unrolled-loop-invariant.c:3:3-3:12 │ │ GraphML: true; server: false │ └──────────────────────────────────────────────────────┘ │ │ i = 0 ▼ ┌──────────────────────────────────────────────────────┐ - │ 11-unrolled-loop-invariant.c:3:3-4:8 (synthetic) │ - │ (11-unrolled-loop-invariant.c:3:10-3:16 (synthetic)) │ - │ YAML loop: 11-unrolled-loop-invariant.c:3:3-4:8 │ + │ 11-unrolled-loop-invariant.c:4:3-5:8 (synthetic) │ + │ (11-unrolled-loop-invariant.c:4:10-4:16 (synthetic)) │ + │ YAML loop: 11-unrolled-loop-invariant.c:4:3-5:8 │ │ GraphML: true; server: false │ - ┌───────────── │ loop: 11-unrolled-loop-invariant.c:3:3-4:8 │ ·┐ + ┌───────────── │ loop: 11-unrolled-loop-invariant.c:4:3-5:8 │ ·┐ │ └──────────────────────────────────────────────────────┘ : │ │ : │ │ Pos(i < 10) : │ ▼ : │ ┌──────────────────────────────────────────────────────┐ : - │ │ 11-unrolled-loop-invariant.c:4:5-4:8 │ : - │ │ (11-unrolled-loop-invariant.c:4:5-4:8) │ : - │ │ YAML loc: 11-unrolled-loop-invariant.c:4:5-4:8 │ : + │ │ 11-unrolled-loop-invariant.c:5:5-5:8 │ : + │ │ (11-unrolled-loop-invariant.c:5:5-5:8) │ : + │ │ YAML loc: 11-unrolled-loop-invariant.c:5:5-5:8 │ : │ │ GraphML: true; server: true │ ·┼····································································┐ │ └──────────────────────────────────────────────────────┘ : : │ │ : : │ │ i = i + 1 : : │ Neg(i < 10) ▼ ▼ : │ ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ : - │ │ 11-unrolled-loop-invariant.c:3:3-4:8 (synthetic) │ : - │ │ (11-unrolled-loop-invariant.c:3:10-3:16 (synthetic)) │ : - │ │ YAML loop: 11-unrolled-loop-invariant.c:3:3-4:8 │ : + │ │ 11-unrolled-loop-invariant.c:4:3-5:8 (synthetic) │ : + │ │ (11-unrolled-loop-invariant.c:4:10-4:16 (synthetic)) │ : + │ │ YAML loop: 11-unrolled-loop-invariant.c:4:3-5:8 │ : │ │ GraphML: true; server: false │ : - │ │ loop: 11-unrolled-loop-invariant.c:3:3-4:8 │ : + │ │ loop: 11-unrolled-loop-invariant.c:4:3-5:8 │ : │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ : │ │ │ ▲ : │ │ Neg(i < 10) │ Pos(i < 10) │ i = i + 1 : │ ▼ │ │ : │ ┌──────────────────────────────────────────────────────┐ │ ┌───────────────────────────────────────────────────┐ : - │ │ 11-unrolled-loop-invariant.c:6:3-6:19 │ │ │ 11-unrolled-loop-invariant.c:4:5-4:8 │ : - │ │ (11-unrolled-loop-invariant.c:6:7-6:12 (synthetic)) │ │ │ (11-unrolled-loop-invariant.c:4:5-4:8) │ : - │ │ YAML loc: 11-unrolled-loop-invariant.c:6:3-6:19 │ │ │ YAML loc: 11-unrolled-loop-invariant.c:4:5-4:8 │ : + │ │ 11-unrolled-loop-invariant.c:7:3-7:19 │ │ │ 11-unrolled-loop-invariant.c:5:5-5:8 │ : + │ │ (11-unrolled-loop-invariant.c:7:7-7:12 (synthetic)) │ │ │ (11-unrolled-loop-invariant.c:5:5-5:8) │ : + │ │ YAML loc: 11-unrolled-loop-invariant.c:7:3-7:19 │ │ │ YAML loc: 11-unrolled-loop-invariant.c:5:5-5:8 │ : └────────────▶ │ GraphML: true; server: false │ └───────────▶ │ GraphML: true; server: true │ ◀┘ └──────────────────────────────────────────────────────┘ └───────────────────────────────────────────────────┘ │ │ j = 0 ▼ ┌──────────────────────────────────────────────────────┐ - │ 11-unrolled-loop-invariant.c:6:3-6:19 (synthetic) │ - │ (11-unrolled-loop-invariant.c:6:14-6:19 (synthetic)) │ + │ 11-unrolled-loop-invariant.c:7:3-7:19 (synthetic) │ + │ (11-unrolled-loop-invariant.c:7:14-7:19 (synthetic)) │ │ GraphML: true; server: false │ └──────────────────────────────────────────────────────┘ │ │ k = 0 ▼ ┌──────────────────────────────────────────────────────┐ ┌───────────────────────────────────────────────────┐ ┌──────────────────┐ - │ 11-unrolled-loop-invariant.c:7:3-11:3 (synthetic) │ │ 11-unrolled-loop-invariant.c:12:3-12:11 │ │ │ - │ (11-unrolled-loop-invariant.c:7:10-7:16 (synthetic)) │ │ (11-unrolled-loop-invariant.c:12:10-12:11) │ │ │ - │ YAML loop: 11-unrolled-loop-invariant.c:7:3-11:3 │ │ YAML loc: 11-unrolled-loop-invariant.c:12:3-12:11 │ │ return of main() │ + │ 11-unrolled-loop-invariant.c:8:3-12:3 (synthetic) │ │ 11-unrolled-loop-invariant.c:13:3-13:11 │ │ │ + │ (11-unrolled-loop-invariant.c:8:10-8:16 (synthetic)) │ │ (11-unrolled-loop-invariant.c:13:10-13:11) │ │ │ + │ YAML loop: 11-unrolled-loop-invariant.c:8:3-12:3 │ │ YAML loc: 11-unrolled-loop-invariant.c:13:3-13:11 │ │ return of main() │ │ GraphML: true; server: false │ Neg(j < 10) │ GraphML: true; server: true │ return 0 │ │ - ┌············· │ loop: 11-unrolled-loop-invariant.c:7:3-11:3 │ ─────────────▶ │ │ ──────────▶ │ │ + ┌············· │ loop: 11-unrolled-loop-invariant.c:8:3-12:3 │ ─────────────▶ │ │ ──────────▶ │ │ : └──────────────────────────────────────────────────────┘ └───────────────────────────────────────────────────┘ └──────────────────┘ : │ ▲ Neg(j < 10) : │ Pos(j < 10) └──────────────────────────────────────────────────────────────────────────────────────────┐ : ▼ │ : ┌──────────────────────────────────────────────────────┐ │ - : │ 11-unrolled-loop-invariant.c:8:5-9:10 (synthetic) │ │ - : │ (11-unrolled-loop-invariant.c:8:12-8:19 (synthetic)) │ │ - : │ YAML loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ │ + : │ 11-unrolled-loop-invariant.c:9:5-10:10 (synthetic) │ │ + : │ (11-unrolled-loop-invariant.c:9:12-9:19 (synthetic)) │ │ + : │ YAML loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ │ : │ GraphML: true; server: false │ │ - ┌──────────────────────────┼───────────── │ loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ ······································································┐ │ + ┌──────────────────────────┼───────────── │ loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ ······································································┐ │ │ : └──────────────────────────────────────────────────────┘ : │ │ : │ : │ │ : │ Pos(k < 100) : │ │ : ▼ : │ │ : ┌──────────────────────────────────────────────────────┐ : │ - │ : │ 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ - │ : │ (11-unrolled-loop-invariant.c:9:7-9:10) │ : │ - │ : │ YAML loc: 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ + │ : │ 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ + │ : │ (11-unrolled-loop-invariant.c:10:7-10:10) │ : │ + │ : │ YAML loc: 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ │ : │ GraphML: true; server: true │ ······································································┼············┐ │ │ : └──────────────────────────────────────────────────────┘ : : │ │ : │ : : │ │ : │ k = k + 1 ┌────────────────────────────────────────────────────┼────────────┼────────────────────────┼─────────────┐ │ : ▼ │ : : │ │ │ : ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ : : │ │ - │ : │ 11-unrolled-loop-invariant.c:8:5-9:10 (synthetic) │ : : │ │ - │ : │ (11-unrolled-loop-invariant.c:8:12-8:19 (synthetic)) │ : : │ │ - │ : │ YAML loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ : : │ │ + │ : │ 11-unrolled-loop-invariant.c:9:5-10:10 (synthetic) │ : : │ │ + │ : │ (11-unrolled-loop-invariant.c:9:12-9:19 (synthetic)) │ : : │ │ + │ : │ YAML loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ : : │ │ │ : │ GraphML: true; server: false │ : : │ │ - │ : │ loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ : : │ │ + │ : │ loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ : : │ │ │ : └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ : : │ │ │ : │ : ▲ : : │ │ │ : │ Pos(k < 100) : │ k = k + 1 : : │ │ │ : ▼ : │ : : │ │ │ : ┌──────────────────────────────────────────────────────┐ : │ : : │ │ - │ : │ 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ : : │ │ - │ : │ (11-unrolled-loop-invariant.c:9:7-9:10) │ : │ : : │ │ - │ : │ YAML loc: 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ : : │ │ + │ : │ 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ : : │ │ + │ : │ (11-unrolled-loop-invariant.c:10:7-10:10) │ : │ : : │ │ + │ : │ YAML loc: 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ : : │ │ │ : │ GraphML: true; server: true │ ─┼───────────────┘ : : │ │ │ : └──────────────────────────────────────────────────────┘ : : : │ │ │ : : : : : │ │ ┌────┘ : : : : : │ │ │ : ▼ : : : │ │ │ : ┌──────────────────────────────────────────────────────┐ : : : │ │ - │ : │ 11-unrolled-loop-invariant.c:9:7-9:10 │ : : : │ │ - │ : │ (11-unrolled-loop-invariant.c:9:7-9:10) │ : : : │ │ - │ : │ YAML loc: 11-unrolled-loop-invariant.c:9:7-9:10 │ : : : │ │ + │ : │ 11-unrolled-loop-invariant.c:10:7-10:10 │ : : : │ │ + │ : │ (11-unrolled-loop-invariant.c:10:7-10:10) │ : : : │ │ + │ : │ YAML loc: 11-unrolled-loop-invariant.c:10:7-10:10 │ : : : │ │ │ ┌··························┼············▶ │ GraphML: true; server: true │ ◀┼───────────────┐ : : │ │ │ : : └──────────────────────────────────────────────────────┘ : │ : : │ │ │ : : │ : │ : : │ │ │ : : │ k = k + 1 : │ Pos(k < 100) : : │ │ │ : : ▼ ▼ │ : : │ │ │ : : ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ : : │ │ - │ : : │ 11-unrolled-loop-invariant.c:8:5-9:10 (synthetic) │ : : │ │ - │ : : │ (11-unrolled-loop-invariant.c:8:12-8:19 (synthetic)) │ : : │ │ - │ : : │ YAML loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ : : │ │ + │ : : │ 11-unrolled-loop-invariant.c:9:5-10:10 (synthetic) │ : : │ │ + │ : : │ (11-unrolled-loop-invariant.c:9:12-9:19 (synthetic)) │ : : │ │ + │ : : │ YAML loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ : : │ │ │ : k = k + 1 : │ GraphML: true; server: false │ : : │ │ - │ : ┌─────────────────────┼────────────▶ │ loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ ◀┼············┼···················┐ │ │ + │ : ┌─────────────────────┼────────────▶ │ loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ ◀┼············┼···················┐ │ │ │ : │ : └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ : : : │ │ │ : │ : │ : : : │ │ │ : │ : │ Neg(k < 100) : : : │ │ │ : │ : ▼ : : : │ │ │ : │ : ┌──────────────────────────────────────────────────────┐ : : : │ │ - │ : │ : │ 11-unrolled-loop-invariant.c:10:5-10:8 │ : : : │ │ - │ : │ : │ (11-unrolled-loop-invariant.c:10:5-10:8) │ : : : │ │ - │ : │ : │ YAML loc: 11-unrolled-loop-invariant.c:10:5-10:8 │ : : : │ │ + │ : │ : │ 11-unrolled-loop-invariant.c:11:5-11:8 │ : : : │ │ + │ : │ : │ (11-unrolled-loop-invariant.c:11:5-11:8) │ : : : │ │ + │ : │ : │ YAML loc: 11-unrolled-loop-invariant.c:11:5-11:8 │ : : : │ │ │ : │ ┌────────────────┼────────────▶ │ GraphML: true; server: true │ ◀·····································································┼············┼···················┼····┼·············┼····┐ │ : │ │ : └──────────────────────────────────────────────────────┘ : : : │ │ : │ : │ │ : │ : : : │ │ : │ : │ │ : │ j = j + 1 ┌────────────────────────────────────────────────────┼────────────┼───────────────────┼────┘ │ : │ : │ │ : ▼ │ : : : │ : │ : │ │ : ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ : : : │ : - │ : │ │ : │ 11-unrolled-loop-invariant.c:7:3-11:3 (synthetic) │ : : : │ : - │ : │ │ : │ (11-unrolled-loop-invariant.c:7:10-7:16 (synthetic)) │ : : : │ : - │ : │ │ Neg(k < 100) : │ YAML loop: 11-unrolled-loop-invariant.c:7:3-11:3 │ : : : │ : + │ : │ │ : │ 11-unrolled-loop-invariant.c:8:3-12:3 (synthetic) │ : : : │ : + │ : │ │ : │ (11-unrolled-loop-invariant.c:8:10-8:16 (synthetic)) │ : : : │ : + │ : │ │ Neg(k < 100) : │ YAML loop: 11-unrolled-loop-invariant.c:8:3-12:3 │ : : : │ : │ : │ │ : │ GraphML: true; server: false │ : : : │ : - │ : │ │ └············▶ │ loop: 11-unrolled-loop-invariant.c:7:3-11:3 │ ◀┼────────────┼───────────────────┼────┐ │ : + │ : │ │ └············▶ │ loop: 11-unrolled-loop-invariant.c:8:3-12:3 │ ◀┼────────────┼───────────────────┼────┐ │ : │ : │ │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ : : : │ │ : │ : │ │ │ : : : │ │ : │ : │ │ │ Pos(j < 10) : : : │ │ : │ : │ │ ▼ : : : │ │ : │ : │ │ ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ : : : │ │ : - │ : │ │ │ 11-unrolled-loop-invariant.c:8:5-9:10 (synthetic) │ : : : │ │ : - │ : │ │ │ (11-unrolled-loop-invariant.c:8:12-8:19 (synthetic)) │ : : : │ │ : - │ : │ │ │ YAML loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ : : : │ j = j + 1 │ : + │ : │ │ │ 11-unrolled-loop-invariant.c:9:5-10:10 (synthetic) │ : : : │ │ : + │ : │ │ │ (11-unrolled-loop-invariant.c:9:12-9:19 (synthetic)) │ : : : │ │ : + │ : │ │ │ YAML loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ : : : │ j = j + 1 │ : │ : │ │ │ GraphML: true; server: false │ : : : │ │ : - │ : │ └────────────────────────────── │ loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ ◀┘ : : │ │ : + │ : │ └────────────────────────────── │ loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ ◀┘ : : │ │ : │ : │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ : : │ │ : │ : │ │ : : : │ │ : │ : │ │ Pos(k < 100) └·································································┼···················┘ │ │ : │ : │ ▼ : │ │ : │ : │ ┌──────────────────────────────────────────────────────┐ : │ │ : - │ : │ │ 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ │ : - │ : │ │ (11-unrolled-loop-invariant.c:9:7-9:10) │ : │ │ : - │ : │ │ YAML loc: 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ │ : + │ : │ │ 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ │ : + │ : │ │ (11-unrolled-loop-invariant.c:10:7-10:10) │ : │ │ : + │ : │ │ YAML loc: 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ │ : │ : └─────────────────────────────────── │ GraphML: true; server: true │ ◀··················································································┘ ┌····┼·············┼····┘ │ : └──────────────────────────────────────────────────────┘ : │ │ │ : : : │ │ @@ -174,42 +174,42 @@ │ ┌·····························································································································································┘ │ │ │ : │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ - │ │ 11-unrolled-loop-invariant.c:10:5-10:8 │ │ │ - │ │ (11-unrolled-loop-invariant.c:10:5-10:8) │ │ │ - │ Neg(k < 100) │ YAML loc: 11-unrolled-loop-invariant.c:10:5-10:8 │ │ │ + │ │ 11-unrolled-loop-invariant.c:11:5-11:8 │ │ │ + │ │ (11-unrolled-loop-invariant.c:11:5-11:8) │ │ │ + │ Neg(k < 100) │ YAML loc: 11-unrolled-loop-invariant.c:11:5-11:8 │ │ │ └────────────────────────────────────────────▶ │ GraphML: true; server: true │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────┘ │ ▲ Neg(k < 100) │ └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ $ goblint --set lib.activated '[]' --set exp.unrolling-factor 5 --enable ana.int.interval --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant", "loop_invariant"]' 11-unrolled-loop-invariant.c - [Info] unrolling loop at 11-unrolled-loop-invariant.c:3:3-4:8 with factor 5 - [Info] unrolling loop at 11-unrolled-loop-invariant.c:8:5-9:10 with factor 5 - [Info] unrolling loop at 11-unrolled-loop-invariant.c:7:3-11:3 with factor 5 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:4:3-5:8 with factor 5 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:9:5-10:10 with factor 5 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:8:3-12:3 with factor 5 [Info][Deadcode] Logical lines of code (LLoC) summary: live: 10 dead: 0 total lines: 10 - [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:7:10-7:16) - [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:7:10-7:16) - [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:3:10-3:16) - [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:3:10-3:16) - [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:3:10-3:16) - [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:3:10-3:16) - [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:3:10-3:16) - [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:7:10-7:16) - [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:7:10-7:16) - [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:7:10-7:16) + [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) + [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) + [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:4:10-4:16) + [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:4:10-4:16) + [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:4:10-4:16) + [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:4:10-4:16) + [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:4:10-4:16) + [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) + [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) + [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) [Info][Witness] witness generation summary: total generation entries: 16 @@ -218,7 +218,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 8 + line: 9 column: 5 function: main loop_invariant: @@ -229,7 +229,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 8 + line: 9 column: 5 function: main loop_invariant: @@ -243,7 +243,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 7 + line: 8 column: 3 function: main loop_invariant: @@ -254,7 +254,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 7 + line: 8 column: 3 function: main loop_invariant: @@ -266,7 +266,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 3 + line: 4 column: 3 function: main loop_invariant: @@ -278,7 +278,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 12 + line: 13 column: 3 function: main location_invariant: @@ -289,7 +289,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 12 + line: 13 column: 3 function: main location_invariant: @@ -300,7 +300,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 12 + line: 13 column: 3 function: main location_invariant: @@ -311,7 +311,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 10 + line: 11 column: 5 function: main location_invariant: @@ -322,7 +322,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 10 + line: 11 column: 5 function: main location_invariant: @@ -333,7 +333,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 10 + line: 11 column: 5 function: main location_invariant: @@ -345,7 +345,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 9 + line: 10 column: 7 function: main location_invariant: @@ -356,7 +356,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 9 + line: 10 column: 7 function: main location_invariant: @@ -367,7 +367,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 9 + line: 10 column: 7 function: main location_invariant: @@ -379,7 +379,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 6 + line: 7 column: 3 function: main location_invariant: @@ -390,7 +390,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 4 + line: 5 column: 5 function: main location_invariant: diff --git a/tests/regression/56-witness/46-top-bool-invariant.c b/tests/regression/56-witness/46-top-bool-invariant.c index 2c90a55a2d..a11fea0991 100644 --- a/tests/regression/56-witness/46-top-bool-invariant.c +++ b/tests/regression/56-witness/46-top-bool-invariant.c @@ -1,5 +1,5 @@ // PARAM: --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --enable ana.int.def_exc --enable ana.int.interval --enable ana.int.enums --enable ana.int.congruence --enable ana.int.interval_set --disable witness.invariant.inexact-type-bounds - +// CRAM int main() { _Bool x; return 0; diff --git a/tests/regression/56-witness/47-top-int-invariant.c b/tests/regression/56-witness/47-top-int-invariant.c index 45f6106ce8..ef39ecd9b2 100644 --- a/tests/regression/56-witness/47-top-int-invariant.c +++ b/tests/regression/56-witness/47-top-int-invariant.c @@ -1,5 +1,5 @@ // PARAM: --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --enable ana.int.def_exc --enable ana.int.interval --enable ana.int.enums --enable ana.int.congruence --enable ana.int.interval_set --disable witness.invariant.inexact-type-bounds - +// CRAM int main() { int x; return 0; diff --git a/tests/regression/56-witness/52-witness-lifter-ps2.c b/tests/regression/56-witness/52-witness-lifter-ps2.c index bcb7c1410c..1eb3495235 100644 --- a/tests/regression/56-witness/52-witness-lifter-ps2.c +++ b/tests/regression/56-witness/52-witness-lifter-ps2.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --enable witness.graphml.enabled --set ana.specification 'CHECK( init(main()), LTL(G valid-memtrack) )' --set ana.activated[+] memLeak --set ana.path_sens[+] memLeak --set ana.malloc.unique_address_count 1 +// NOCRASH struct _twoIntsStruct { int intOne ; int intTwo ; diff --git a/tests/regression/56-witness/53-witness-lifter-ps3.c b/tests/regression/56-witness/53-witness-lifter-ps3.c index 06b73b3888..ff368a88fc 100644 --- a/tests/regression/56-witness/53-witness-lifter-ps3.c +++ b/tests/regression/56-witness/53-witness-lifter-ps3.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --enable witness.graphml.enabled --set ana.specification 'CHECK( init(main()), LTL(G valid-memtrack) )' --set ana.activated[+] memLeak --set ana.path_sens[+] memLeak --set ana.malloc.unique_address_count 1 +// NOCRASH struct _twoIntsStruct { int intOne ; int intTwo ; diff --git a/tests/regression/61-evalAssert/01-union_evalAssert.c b/tests/regression/61-evalAssert/01-union_evalAssert.c index 22e72e0e51..150d1fada2 100644 --- a/tests/regression/61-evalAssert/01-union_evalAssert.c +++ b/tests/regression/61-evalAssert/01-union_evalAssert.c @@ -1,5 +1,5 @@ // PARAM: --set trans.activated[+] "assert" - +// NOCHECK // Running the assert transformation on this test used to yield code that cannot be compiled with gcc, due to superfluous offsets on a pointer struct s { int a; diff --git a/tests/regression/63-affeq/14-norm_inv.c b/tests/regression/63-affeq/14-norm_inv.c index d93fa00cd4..df144fc402 100644 --- a/tests/regression/63-affeq/14-norm_inv.c +++ b/tests/regression/63-affeq/14-norm_inv.c @@ -3,6 +3,7 @@ // Normalization should be triggered when an invertible expression is assigned. // No asserts, likely fixpoint regression. // TODO: used to have list-based matrices, issue was only with those? +// NOCHECK int main() { int A, B; int r, d, p, q; diff --git a/tests/regression/63-affeq/17-verify.c b/tests/regression/63-affeq/17-verify.c index 7ac1b202e4..2ae7d338ba 100644 --- a/tests/regression/63-affeq/17-verify.c +++ b/tests/regression/63-affeq/17-verify.c @@ -1,5 +1,5 @@ //SKIP PARAM: --set ana.activated[+] affeq --sem.int.signed_overflow "assume_none" --enable ana.int.interval -// Error in leq check led to verify error +// FIXPOINT: Error in leq check led to verify error int main() { int n, a, b; diff --git a/tests/regression/66-interval-set-one/00-was_problematic_2.c b/tests/regression/66-interval-set-one/00-was_problematic_2.c index e5b3938a76..187e6f39df 100644 --- a/tests/regression/66-interval-set-one/00-was_problematic_2.c +++ b/tests/regression/66-interval-set-one/00-was_problematic_2.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +// NOCHECK int main(void) { int arr[260]; diff --git a/tests/regression/66-interval-set-one/03-was_problematic_3.c b/tests/regression/66-interval-set-one/03-was_problematic_3.c index 59ce6e5cb6..2a8bd4bc46 100644 --- a/tests/regression/66-interval-set-one/03-was_problematic_3.c +++ b/tests/regression/66-interval-set-one/03-was_problematic_3.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +// NOCHECK struct some_struct { int dir[7]; diff --git a/tests/regression/66-interval-set-one/14-no-int-context.c b/tests/regression/66-interval-set-one/14-no-int-context.c index 69d2aa2aa7..d701caf59e 100644 --- a/tests/regression/66-interval-set-one/14-no-int-context.c +++ b/tests/regression/66-interval-set-one/14-no-int-context.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.interval_set --set solver slr3t --disable ana.base.context.int - +// NOCHECK int f (int i) { // -2 return i+1; } // -3 void g(int j) { // -4 diff --git a/tests/regression/66-interval-set-one/39-calls.c b/tests/regression/66-interval-set-one/39-calls.c index 67ff46ad77..acfade3084 100644 --- a/tests/regression/66-interval-set-one/39-calls.c +++ b/tests/regression/66-interval-set-one/39-calls.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --set ana.base.arrays.domain partitioned // Variable-sized arrays +// NOCHECK void foo(int n, int a[n]); void foo2(int n, int a[30][n]); void foo3(int n, int a[n][30]); diff --git a/tests/regression/66-interval-set-one/93-enum.c b/tests/regression/66-interval-set-one/93-enum.c index 5d2b45043d..ca769fb126 100644 --- a/tests/regression/66-interval-set-one/93-enum.c +++ b/tests/regression/66-interval-set-one/93-enum.c @@ -1,4 +1,5 @@ // PARAM: --disable ana.int.interval_set --disable ana.int.def_exc --enable ana.int.enums +// NOCHECK void main(){ int n = 1; for (; n; n++) { // fixed point not reached here diff --git a/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c b/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c index 8d56fa0e3c..f5291fdcdc 100644 --- a/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c +++ b/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c @@ -1,6 +1,7 @@ // PARAM: --enable ana.int.def_exc --enable ana.int.interval_set --enable ana.sv-comp.functions --set sem.int.signed_overflow assume_none --set ana.int.refinement never // used to crash in branch when is_bool returned true, but to_bool returned None on (0,[1,1]) // manually minimized from sv-benchmarks/c/recursive/MultCommutative-2.c +// NOCHECK extern int __VERIFIER_nondet_int(void); void f(int m) { diff --git a/tests/regression/67-interval-sets-two/04-unsupported.c b/tests/regression/67-interval-sets-two/04-unsupported.c index 97ce11258a..4127da4d8b 100644 --- a/tests/regression/67-interval-sets-two/04-unsupported.c +++ b/tests/regression/67-interval-sets-two/04-unsupported.c @@ -1,6 +1,7 @@ // PARAM: --enable ana.int.interval_set --disable exp.fast_global_inits --set ana.base.arrays.domain partitioned // This is just to test that the analysis does not cause problems for features that are not explicitly dealt with +// NOCHECK: what problems? int main(void) { callok(); } diff --git a/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c b/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c index af46023135..a5e5e937cb 100644 --- a/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c +++ b/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set --set solver slr3t +// NOCHECK #include pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/tests/regression/67-interval-sets-two/15-interval-bot.c b/tests/regression/67-interval-sets-two/15-interval-bot.c index ab7d043b92..6ab90062ae 100644 --- a/tests/regression/67-interval-sets-two/15-interval-bot.c +++ b/tests/regression/67-interval-sets-two/15-interval-bot.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.interval_set --enable ana.int.def_exc - +// NOCHECK int main(){ unsigned long long a ; diff --git a/tests/regression/67-interval-sets-two/24-arithmetic-bot.c b/tests/regression/67-interval-sets-two/24-arithmetic-bot.c index f238788bb3..f9a2d0086a 100644 --- a/tests/regression/67-interval-sets-two/24-arithmetic-bot.c +++ b/tests/regression/67-interval-sets-two/24-arithmetic-bot.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval_set --enable ana.int.def_exc // from: ldv-linux-3.0/usb_urb-drivers-vhost-vhost_net.ko.cil.out.i +// NOCHECK typedef unsigned long long u64; int main( ) diff --git a/tests/regression/67-interval-sets-two/31-ptrdiff.c b/tests/regression/67-interval-sets-two/31-ptrdiff.c index d7bd4667df..d6135f8734 100644 --- a/tests/regression/67-interval-sets-two/31-ptrdiff.c +++ b/tests/regression/67-interval-sets-two/31-ptrdiff.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] var_eq +// NOCHECK int *tmp; int main () diff --git a/tests/regression/67-interval-sets-two/44-comparision-bot.c b/tests/regression/67-interval-sets-two/44-comparision-bot.c index 5b3622828a..eb6e90b5ea 100644 --- a/tests/regression/67-interval-sets-two/44-comparision-bot.c +++ b/tests/regression/67-interval-sets-two/44-comparision-bot.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set --enable ana.int.def_exc +// NOCHECK #include int main(){ int a = 0; diff --git a/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c b/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c index e1345155d9..df1d7346bd 100644 --- a/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c +++ b/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set +// NOCHECK #include #include diff --git a/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c b/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c index b63ebd6fab..1a709243cb 100644 --- a/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c +++ b/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set +// NOCHECK #include #include diff --git a/tests/regression/71-doublelocking/15-rec-dyn-nested.c b/tests/regression/71-doublelocking/15-rec-dyn-nested.c index d5dac9cd81..472783a9f3 100644 --- a/tests/regression/71-doublelocking/15-rec-dyn-nested.c +++ b/tests/regression/71-doublelocking/15-rec-dyn-nested.c @@ -1,5 +1,5 @@ // PARAM: --set ana.activated[+] 'pthreadMutexType' -// Check we don't have a stack overflow because of tracking multiplicities +// NOCRASH: Check we don't have a stack overflow because of tracking multiplicities #define _GNU_SOURCE #include #include diff --git a/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c b/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c index cc9819950f..93d4d6f307 100644 --- a/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c +++ b/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c @@ -1,4 +1,5 @@ //PARAM: --set ana.activated[+] useAfterFree +// NOCHECK #include int *global; From 5de8823149be753d59e7e3f55c2eb0975e8fd09e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Jul 2024 18:52:31 +0300 Subject: [PATCH 293/566] Add NOCHECK and CRAM annotations to incremental tests --- tests/incremental/00-basic/01-global.c | 1 + tests/incremental/00-basic/01-global.patch | 1 + tests/incremental/00-basic/04-rename_ids.c | 1 + tests/incremental/00-basic/08-refine_interval_with_def_exc.c | 1 + tests/incremental/00-basic/12-rec-type.c | 2 +- tests/incremental/00-basic/12-rec-type.patch | 4 ++-- tests/incremental/00-basic/15-reluctant-test.c | 2 +- tests/incremental/04-var-rename/01-rename_and_shuffle.c | 2 +- tests/incremental/04-var-rename/01-rename_and_shuffle.patch | 2 +- tests/incremental/04-var-rename/02-rename_with_usage.c | 2 +- tests/incremental/04-var-rename/02-rename_with_usage.patch | 2 +- tests/incremental/04-var-rename/04-renamed_param.c | 1 + tests/incremental/04-var-rename/04-renamed_param.patch | 1 + .../04-var-rename/05-renamed_param_usage_changed.c | 2 +- .../04-var-rename/05-renamed_param_usage_changed.patch | 2 +- tests/incremental/05-method-rename/00-simple_rename.c | 2 +- tests/incremental/05-method-rename/00-simple_rename.patch | 2 +- tests/incremental/05-method-rename/01-dependent_rename.c | 2 +- tests/incremental/05-method-rename/01-dependent_rename.patch | 2 +- .../05-method-rename/02-cyclic_rename_dependency.c | 2 +- .../05-method-rename/02-cyclic_rename_dependency.patch | 2 +- tests/incremental/05-method-rename/03-cyclic_with_swap.c | 2 +- tests/incremental/05-method-rename/03-cyclic_with_swap.patch | 2 +- tests/incremental/05-method-rename/04-deep_change.c | 2 +- tests/incremental/05-method-rename/04-deep_change.patch | 2 +- tests/incremental/05-method-rename/05-common_rename.c | 2 +- tests/incremental/05-method-rename/05-common_rename.patch | 2 +- tests/incremental/05-method-rename/06-recursive_rename.c | 1 + tests/incremental/05-method-rename/06-recursive_rename.patch | 3 ++- tests/incremental/06-glob-var-rename/00-simple_rename.c | 2 +- tests/incremental/06-glob-var-rename/00-simple_rename.patch | 2 +- .../06-glob-var-rename/01-duplicate_local_global.c | 2 +- .../06-glob-var-rename/01-duplicate_local_global.patch | 2 +- tests/incremental/06-glob-var-rename/02-add_new_gvar.c | 2 +- tests/incremental/06-glob-var-rename/02-add_new_gvar.patch | 2 +- tests/incremental/11-restart/00-justglob.c | 1 + 36 files changed, 38 insertions(+), 29 deletions(-) diff --git a/tests/incremental/00-basic/01-global.c b/tests/incremental/00-basic/01-global.c index 8eac5b92a1..5b3a1feb6a 100644 --- a/tests/incremental/00-basic/01-global.c +++ b/tests/incremental/00-basic/01-global.c @@ -1,5 +1,6 @@ // Previosuly, the function was erroneously not reanalyzed when the global initializer/the start state changed // when hash-consing is activated. +// NOCHECK int g = 0; int main(){ diff --git a/tests/incremental/00-basic/01-global.patch b/tests/incremental/00-basic/01-global.patch index 2b86496f5a..9c74751174 100644 --- a/tests/incremental/00-basic/01-global.patch +++ b/tests/incremental/00-basic/01-global.patch @@ -3,6 +3,7 @@ @@ -1,6 +1,6 @@ // Previosuly, the function was erroneously not reanalyzed when the global initializer/the start state changed // when hash-consing is activated. + // NOCHECK -int g = 0; +int g = 35; diff --git a/tests/incremental/00-basic/04-rename_ids.c b/tests/incremental/00-basic/04-rename_ids.c index e5c9727edc..7f2884854d 100644 --- a/tests/incremental/00-basic/04-rename_ids.c +++ b/tests/incremental/00-basic/04-rename_ids.c @@ -1,3 +1,4 @@ +// NOCHECK int a; void b(); void c(); diff --git a/tests/incremental/00-basic/08-refine_interval_with_def_exc.c b/tests/incremental/00-basic/08-refine_interval_with_def_exc.c index da6cb9dd65..3fcf60e83a 100644 --- a/tests/incremental/00-basic/08-refine_interval_with_def_exc.c +++ b/tests/incremental/00-basic/08-refine_interval_with_def_exc.c @@ -1,3 +1,4 @@ +// NOCHECK struct input_state; typedef struct input_state input_state; struct input_state { diff --git a/tests/incremental/00-basic/12-rec-type.c b/tests/incremental/00-basic/12-rec-type.c index 7c862a107a..1c92e6f94b 100644 --- a/tests/incremental/00-basic/12-rec-type.c +++ b/tests/incremental/00-basic/12-rec-type.c @@ -4,7 +4,7 @@ typedef struct s_t s_t; union union_t { int i; - s_t *s[sizeof(s_t *)]; // This caused problems + s_t *s[sizeof(s_t *)]; // NOCRASH: This caused problems }; struct s_t { diff --git a/tests/incremental/00-basic/12-rec-type.patch b/tests/incremental/00-basic/12-rec-type.patch index 2a5bd8f478..5d00033464 100644 --- a/tests/incremental/00-basic/12-rec-type.patch +++ b/tests/incremental/00-basic/12-rec-type.patch @@ -5,8 +5,8 @@ index 7c862a107..a043e249c 100644 @@ -2,6 +2,7 @@ struct s_t ; typedef struct s_t s_t; - + +// Dummy change union union_t { int i; - s_t *s[sizeof(s_t *)]; // This caused problems + s_t *s[sizeof(s_t *)]; // NOCRASH: This caused problems diff --git a/tests/incremental/00-basic/15-reluctant-test.c b/tests/incremental/00-basic/15-reluctant-test.c index 6328061b29..a0c96977b7 100644 --- a/tests/incremental/00-basic/15-reluctant-test.c +++ b/tests/incremental/00-basic/15-reluctant-test.c @@ -1,5 +1,5 @@ #include -// This test used to resulted in an unreached fixpoint in the incremental implementation. +// FIXPOINT: This test used to resulted in an unreached fixpoint in the incremental implementation. int g = 3; diff --git a/tests/incremental/04-var-rename/01-rename_and_shuffle.c b/tests/incremental/04-var-rename/01-rename_and_shuffle.c index 7d6ea81e6f..0acafcae90 100644 --- a/tests/incremental/04-var-rename/01-rename_and_shuffle.c +++ b/tests/incremental/04-var-rename/01-rename_and_shuffle.c @@ -1,5 +1,5 @@ #include - +// CRAM // a is renamed to c, but the usage of a is replaced by b (semantic changes) int main() { int a = 0; diff --git a/tests/incremental/04-var-rename/01-rename_and_shuffle.patch b/tests/incremental/04-var-rename/01-rename_and_shuffle.patch index 94e27d9a80..fa6342b0ef 100644 --- a/tests/incremental/04-var-rename/01-rename_and_shuffle.patch +++ b/tests/incremental/04-var-rename/01-rename_and_shuffle.patch @@ -1,7 +1,7 @@ --- tests/incremental/04-var-rename/01-rename_and_shuffle.c +++ tests/incremental/04-var-rename/01-rename_and_shuffle.c @@ -2,10 +2,10 @@ - + // CRAM // a is renamed to c, but the usage of a is replaced by b (semantic changes) int main() { - int a = 0; diff --git a/tests/incremental/04-var-rename/02-rename_with_usage.c b/tests/incremental/04-var-rename/02-rename_with_usage.c index 2c93c487d8..928a8014d1 100644 --- a/tests/incremental/04-var-rename/02-rename_with_usage.c +++ b/tests/incremental/04-var-rename/02-rename_with_usage.c @@ -1,5 +1,5 @@ #include - +// CRAM //a is renamed to c, but its usages stay the same int main() { int a = 0; diff --git a/tests/incremental/04-var-rename/02-rename_with_usage.patch b/tests/incremental/04-var-rename/02-rename_with_usage.patch index 6cfe41bbb1..b6d0e4cb19 100644 --- a/tests/incremental/04-var-rename/02-rename_with_usage.patch +++ b/tests/incremental/04-var-rename/02-rename_with_usage.patch @@ -1,7 +1,7 @@ --- tests/incremental/04-var-rename/02-rename_with_usage.c +++ tests/incremental/04-var-rename/02-rename_with_usage.c @@ -2,10 +2,10 @@ - + // CRAM //a is renamed to c, but its usages stay the same int main() { - int a = 0; diff --git a/tests/incremental/04-var-rename/04-renamed_param.c b/tests/incremental/04-var-rename/04-renamed_param.c index 770af2683c..1d45695b1b 100644 --- a/tests/incremental/04-var-rename/04-renamed_param.c +++ b/tests/incremental/04-var-rename/04-renamed_param.c @@ -1,4 +1,5 @@ // function param is renamed (no semantic changes) +// CRAM void method(int a) { int c = a; } diff --git a/tests/incremental/04-var-rename/04-renamed_param.patch b/tests/incremental/04-var-rename/04-renamed_param.patch index 50a9b69f6a..7753ded6df 100644 --- a/tests/incremental/04-var-rename/04-renamed_param.patch +++ b/tests/incremental/04-var-rename/04-renamed_param.patch @@ -1,6 +1,7 @@ --- tests/incremental/04-var-rename/04-renamed_param.c +++ tests/incremental/04-var-rename/04-renamed_param.c @@ -2,5 +2,5 @@ + // CRAM -void method(int a) { - int c = a; +void method(int b) { diff --git a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.c b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.c index aed642566c..1453e5707a 100644 --- a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.c +++ b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.c @@ -1,5 +1,5 @@ //This test should mark foo and main as changed - +// CRAM void foo(int a, int b) { int x = a; int y = b; diff --git a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch index 9ffc2c1cea..7e46542911 100644 --- a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch +++ b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch @@ -2,7 +2,7 @@ +++ tests/incremental/04-var-rename/05-renamed_param_usage_changed.c @@ -1,6 +1,6 @@ //This test should mark foo and main as changed - + // CRAM -void foo(int a, int b) { +void foo(int b, int a) { int x = a; diff --git a/tests/incremental/05-method-rename/00-simple_rename.c b/tests/incremental/05-method-rename/00-simple_rename.c index 5d1e6fe872..0dd7b4ba1c 100644 --- a/tests/incremental/05-method-rename/00-simple_rename.c +++ b/tests/incremental/05-method-rename/00-simple_rename.c @@ -1,5 +1,5 @@ #include - +// CRAM void foo() { printf("foo"); } diff --git a/tests/incremental/05-method-rename/00-simple_rename.patch b/tests/incremental/05-method-rename/00-simple_rename.patch index ed7b40014c..31f0bf18cc 100644 --- a/tests/incremental/05-method-rename/00-simple_rename.patch +++ b/tests/incremental/05-method-rename/00-simple_rename.patch @@ -2,7 +2,7 @@ +++ tests/incremental/05-method-rename/00-simple_rename.c @@ -1,10 +1,10 @@ #include - + // CRAM -void foo() { +void bar() { printf("foo"); diff --git a/tests/incremental/05-method-rename/01-dependent_rename.c b/tests/incremental/05-method-rename/01-dependent_rename.c index 66c1a5a634..c5c43d5cde 100644 --- a/tests/incremental/05-method-rename/01-dependent_rename.c +++ b/tests/incremental/05-method-rename/01-dependent_rename.c @@ -1,5 +1,5 @@ #include - +// CRAM void fun1() { printf("fun1"); } diff --git a/tests/incremental/05-method-rename/01-dependent_rename.patch b/tests/incremental/05-method-rename/01-dependent_rename.patch index f3a4a9a3f8..3712f75059 100644 --- a/tests/incremental/05-method-rename/01-dependent_rename.patch +++ b/tests/incremental/05-method-rename/01-dependent_rename.patch @@ -2,7 +2,7 @@ +++ tests/incremental/05-method-rename/01-dependent_rename.c @@ -1,14 +1,14 @@ #include - + // CRAM -void fun1() { +void bar1() { printf("fun1"); diff --git a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.c b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.c index 331a5e25cb..41813818cb 100644 --- a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.c +++ b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.c @@ -1,5 +1,5 @@ #include - +// CRAM void foo1(int c) { if (c < 10) foo2(c + 1); } diff --git a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch index 7f15d88c3a..3b62220898 100644 --- a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch +++ b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch @@ -1,7 +1,7 @@ --- tests/incremental/05-method-rename/02-cyclic_rename_dependency.c +++ tests/incremental/05-method-rename/02-cyclic_rename_dependency.c @@ -2,14 +2,14 @@ - + // CRAM -void foo1(int c) { - if (c < 10) foo2(c + 1); +void bar1(int c) { diff --git a/tests/incremental/05-method-rename/03-cyclic_with_swap.c b/tests/incremental/05-method-rename/03-cyclic_with_swap.c index 34026afa92..898f15fe6d 100644 --- a/tests/incremental/05-method-rename/03-cyclic_with_swap.c +++ b/tests/incremental/05-method-rename/03-cyclic_with_swap.c @@ -1,5 +1,5 @@ #include - +// CRAM void foo1(int c) { if (c < 10) foo2(c + 1); } diff --git a/tests/incremental/05-method-rename/03-cyclic_with_swap.patch b/tests/incremental/05-method-rename/03-cyclic_with_swap.patch index 0886106162..3373449875 100644 --- a/tests/incremental/05-method-rename/03-cyclic_with_swap.patch +++ b/tests/incremental/05-method-rename/03-cyclic_with_swap.patch @@ -1,7 +1,7 @@ --- tests/incremental/05-method-rename/03-cyclic_with_swap.c +++ tests/incremental/05-method-rename/03-cyclic_with_swap.c @@ -2,13 +2,17 @@ - + // CRAM -void foo1(int c) { - if (c < 10) foo2(c + 1); +void newFun(int c) { diff --git a/tests/incremental/05-method-rename/04-deep_change.c b/tests/incremental/05-method-rename/04-deep_change.c index 80037f934d..4258329f2e 100644 --- a/tests/incremental/05-method-rename/04-deep_change.c +++ b/tests/incremental/05-method-rename/04-deep_change.c @@ -1,5 +1,5 @@ #include - +// CRAM void zap() { printf("zap"); } diff --git a/tests/incremental/05-method-rename/04-deep_change.patch b/tests/incremental/05-method-rename/04-deep_change.patch index 687b8f74bc..6d7ec21bf8 100644 --- a/tests/incremental/05-method-rename/04-deep_change.patch +++ b/tests/incremental/05-method-rename/04-deep_change.patch @@ -2,7 +2,7 @@ +++ tests/incremental/05-method-rename/04-deep_change.c @@ -1,7 +1,7 @@ #include - + // CRAM void zap() { - printf("zap"); + printf("drap"); diff --git a/tests/incremental/05-method-rename/05-common_rename.c b/tests/incremental/05-method-rename/05-common_rename.c index ce72a6dda1..643bba554a 100644 --- a/tests/incremental/05-method-rename/05-common_rename.c +++ b/tests/incremental/05-method-rename/05-common_rename.c @@ -1,5 +1,5 @@ #include - +// CRAM void foo() { printf("foo"); } diff --git a/tests/incremental/05-method-rename/05-common_rename.patch b/tests/incremental/05-method-rename/05-common_rename.patch index 93904d5780..dcf6d15719 100644 --- a/tests/incremental/05-method-rename/05-common_rename.patch +++ b/tests/incremental/05-method-rename/05-common_rename.patch @@ -2,7 +2,7 @@ +++ tests/incremental/05-method-rename/05-common_rename.c @@ -1,20 +1,20 @@ #include - + // CRAM -void foo() { +void bar() { printf("foo"); diff --git a/tests/incremental/05-method-rename/06-recursive_rename.c b/tests/incremental/05-method-rename/06-recursive_rename.c index dc9ac72e94..3d1dd2b9d1 100644 --- a/tests/incremental/05-method-rename/06-recursive_rename.c +++ b/tests/incremental/05-method-rename/06-recursive_rename.c @@ -1,3 +1,4 @@ +// CRAM void foo(int x) { if(x > 1) foo(x - 1); } diff --git a/tests/incremental/05-method-rename/06-recursive_rename.patch b/tests/incremental/05-method-rename/06-recursive_rename.patch index 356f959256..e29be521be 100644 --- a/tests/incremental/05-method-rename/06-recursive_rename.patch +++ b/tests/incremental/05-method-rename/06-recursive_rename.patch @@ -1,6 +1,7 @@ --- tests/incremental/05-method-rename/06-recursive_rename.c +++ tests/incremental/05-method-rename/06-recursive_rename.c -@@ -1,7 +1,7 @@ +@@ -1,8 +1,8 @@ + // CRAM -void foo(int x) { - if(x > 1) foo(x - 1); +void bar(int x) { diff --git a/tests/incremental/06-glob-var-rename/00-simple_rename.c b/tests/incremental/06-glob-var-rename/00-simple_rename.c index 56650e98ed..c06ba3a85a 100644 --- a/tests/incremental/06-glob-var-rename/00-simple_rename.c +++ b/tests/incremental/06-glob-var-rename/00-simple_rename.c @@ -1,5 +1,5 @@ #include - +// CRAM int foo = 1; int main() { diff --git a/tests/incremental/06-glob-var-rename/00-simple_rename.patch b/tests/incremental/06-glob-var-rename/00-simple_rename.patch index 1e0f3b2565..b9d20af358 100644 --- a/tests/incremental/06-glob-var-rename/00-simple_rename.patch +++ b/tests/incremental/06-glob-var-rename/00-simple_rename.patch @@ -2,7 +2,7 @@ +++ tests/incremental/06-glob-var-rename/00-simple_rename.c @@ -1,9 +1,9 @@ #include - + // CRAM -int foo = 1; +int bar = 1; diff --git a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c index 9ad715e50d..35e527225c 100644 --- a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c +++ b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c @@ -1,5 +1,5 @@ #include - +// CRAM int foo = 1; int main() { diff --git a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch index 1d65c5672a..2c44da4cce 100644 --- a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch +++ b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch @@ -2,7 +2,7 @@ +++ tests/incremental/06-glob-var-rename/01-duplicate_local_global.c @@ -1,14 +1,14 @@ #include - + // CRAM -int foo = 1; +int bar = 1; diff --git a/tests/incremental/06-glob-var-rename/02-add_new_gvar.c b/tests/incremental/06-glob-var-rename/02-add_new_gvar.c index 5efe319981..c399e1f784 100644 --- a/tests/incremental/06-glob-var-rename/02-add_new_gvar.c +++ b/tests/incremental/06-glob-var-rename/02-add_new_gvar.c @@ -1,5 +1,5 @@ #include - +// CRAM int myVar = 1; int main() { diff --git a/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch b/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch index f0c145107a..0ca97da64f 100644 --- a/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch +++ b/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch @@ -2,7 +2,7 @@ +++ tests/incremental/06-glob-var-rename/02-add_new_gvar.c @@ -1,8 +1,9 @@ #include - + // CRAM int myVar = 1; +int foo = 1; diff --git a/tests/incremental/11-restart/00-justglob.c b/tests/incremental/11-restart/00-justglob.c index 61b059f8ea..1e35604d3f 100644 --- a/tests/incremental/11-restart/00-justglob.c +++ b/tests/incremental/11-restart/00-justglob.c @@ -1,2 +1,3 @@ int max_domains = 0; int main() {} +// CRAM From 205f058e3dea0aaf17953aef4fd9de5d75aa7a3a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Jul 2024 18:59:56 +0300 Subject: [PATCH 294/566] Error on missing automatic checks in regression tests --- scripts/update_suite.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index 0e90bbf828..e760cdd721 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -379,6 +379,7 @@ def parse_tests (lines) end if tests.empty? then puts "No automatic checks in #{@id} (maybe NOCRASH/FIXPOINT/NOTIMEOUT/CRAM?)" + exit 1 end Tests.new(self, tests, tests_line, todo) end From 8da70467421f2ecb0f0712848da93743a8d6e67a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Jul 2024 19:06:07 +0300 Subject: [PATCH 295/566] Document meta annotations for regression tests --- docs/developer-guide/testing.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/developer-guide/testing.md b/docs/developer-guide/testing.md index 22b0070fa4..a336228fde 100644 --- a/docs/developer-guide/testing.md +++ b/docs/developer-guide/testing.md @@ -69,6 +69,16 @@ Other useful constructs are the following: | `__goblint_check(1); // reachable` | Checks that the line is reachable according
to Goblint results (soundness). | | `__goblint_check(0); // NOWARN (unreachable)` | Checks that the line is unreachable (precision). | +#### Meta +Comments at the end of lines can also indicate metaproperties: + +| Annotation | Expected result/comment | +| ---------- | ----- | +| `NOCRASH` | No analyzer crash | +| `FIXPOINT` | No fixpoint error | +| `NOTIMEOUT` | Analyer terminates | +| `CRAM` | Automatic checks are only in corresponding Cram test | + ## Cram Tests [Cram-style tests](https://dune.readthedocs.io/en/stable/tests.html#cram-tests) are also used to verify that existing functionality hasn't been broken. They check the complete standard output of running the Goblint binary with specified command-line arguments. From 14d51d51fe952f6edad9871350f71cfccaea710d Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 12 Jul 2024 16:58:31 +0200 Subject: [PATCH 296/566] Check overflows when generating witnesses --- src/cdomains/apron/sharedFunctions.apron.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 4809e97917..a9406d48e9 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -274,7 +274,7 @@ struct let expr = ref (fst @@ coeff_to_const false (Linexpr1.get_cst linexpr1)) in let append_summand (c:Coeff.union_5) v = match V.to_cil_varinfo v with - | Some vinfo -> + | Some vinfo when IntDomain.Size.is_cast_injective ~from_type:vinfo.vtype ~to_type:(TInt(ILongLong,[])) -> (* TODO: What to do with variables that have a type that cannot be stored into ILongLong to avoid overflows? *) let var = Cilfacade.mkCast ~e:(Lval(Var vinfo,NoOffset)) ~newt:longlong in let coeff, flip = coeff_to_const true c in @@ -284,6 +284,7 @@ struct else expr := BinOp(PlusA,!expr,prod,longlong) | None -> M.warn ~category:Analyzer "Invariant Apron: cannot convert to cil var: %s" (Var.to_string v); raise Unsupported_Linexpr1 + | _ -> M.warn ~category:Analyzer "Invariant Apron: cannot convert to cil var in overflow preserving manner: %s" (Var.to_string v); raise Unsupported_Linexpr1 in Linexpr1.iter append_summand linexpr1; !expr From 726725b88015547e9aeab38232fe130ab113993f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 17 Jul 2024 11:15:45 +0300 Subject: [PATCH 297/566] Add Printable functions to GobApron.Var --- src/analyses/apron/relationAnalysis.apron.ml | 4 +-- .../apron/affineEqualityDomain.apron.ml | 10 +++--- src/cdomains/apron/gobApron.apron.ml | 9 ++++++ .../apron/linearTwoVarEqualityDomain.apron.ml | 32 +++++++++---------- src/cdomains/apron/sharedFunctions.apron.ml | 4 +-- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index a42d78d71a..6dd2a65cee 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -295,7 +295,7 @@ struct (* there should be smarter ways to do this, e.g. by keeping track of which values are written etc. ... *) (* See, e.g, Beckschulze E, Kowalewski S, Brauer J (2012) Access-based localization for octagons. Electron Notes Theor Comput Sci 287:29–40 *) (* Also, a local *) - let vname = Apron.Var.to_string var in + let vname = GobApron.Var.show var in let locals = fundec.sformals @ fundec.slocals in match List.find_opt (fun v -> VM.var_name (Local v) = vname) locals with (* TODO: optimize *) | None -> true @@ -418,7 +418,7 @@ struct in let any_local_reachable = any_local_reachable fundec reachable_from_args in let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in - if M.tracing then M.tracel "combine-rel" "relation remove vars: %a" (docList (fun v -> Pretty.text (Apron.Var.to_string v))) arg_vars; + if M.tracing then M.tracel "combine-rel" "relation remove vars: %a" (docList (GobApron.Var.pretty ())) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) let tainted = f_ask.f Queries.MayBeTainted in let tainted_vars = TaintPartialContexts.conv_varset tainted in diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 8bb99f2264..ec0bb9b940 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -181,7 +181,7 @@ struct else if Z.lt coeff Z.minus_one then Z.to_string coeff else Format.asprintf "+%s" (Z.to_string coeff) in - coeff_str ^ Var.to_string var + coeff_str ^ Var.show var in let const_to_str vl = if Z.equal vl Z.zero then @@ -429,8 +429,8 @@ struct let assign_exp ask t var exp no_ov = let res = assign_exp ask t var exp no_ov in - if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s" - (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; + if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %a \n exp: %a\n no_ov: %b -> \n %s" + (show t) Var.pretty var d_exp exp (Lazy.force no_ov) (show res); res let assign_var (t: VarManagement(Vc)(Mx).t) v v' = @@ -440,7 +440,7 @@ struct let assign_var t v v' = let res = assign_var t v v' in - if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s" (show t) (Var.to_string v) (Var.to_string v') (show res) ; + if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %a \n v': %a\n -> %s" (show t) Var.pretty v Var.pretty v' (show res); res let assign_var_parallel t vv's = @@ -498,7 +498,7 @@ struct let substitute_exp ask t var exp no_ov = let res = substitute_exp ask t var exp no_ov in - if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s" (show t) (Var.to_string var) d_exp exp (show res); + if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %a \n exp: %a \n -> \n %s" (show t) Var.pretty var d_exp exp (show res); res let substitute_exp ask t var exp no_ov = timing_wrap "substitution" (substitute_exp ask t var exp) no_ov diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml index f2322c1473..fca28ff8f0 100644 --- a/src/cdomains/apron/gobApron.apron.ml +++ b/src/cdomains/apron/gobApron.apron.ml @@ -11,6 +11,15 @@ end module Var = struct include Var + + let pp = print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) + let equal x y = Var.compare x y = 0 end diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index bd6b81402a..c13aca60ea 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -158,7 +158,7 @@ module EqualitiesConjunction = struct let (newref,offs,divi) = (get_rhs d head) in let (coeff,y) = BatOption.get newref in let (y,yrhs) = inverse head (coeff,y,offs,divi) in (* reassemble yrhs out of components *) - let shifted_cluster = (List.fold (fun map i -> + let shifted_cluster = (List.fold (fun map i -> let irhs = (get_rhs d i) in (* old entry is i = irhs *) Rhs.subst yrhs y irhs |> (* new entry for i is irhs [yrhs/y] *) set_rhs map i @@ -222,7 +222,7 @@ module EqualitiesConjunction = struct | Some (coeff,j), ((Some (coeff1,h1), o1, divi1) as oldi)-> (match get_rhs ts j with (* ts[x_j]=o2/d2 ========> ... *) - | (None , o2, divi2) -> + | (None , o2, divi2) -> let newxi = Rhs.subst (None,o2,divi2) j (Some (coeff,j),offs,divi) in let newxh1 = snd @@ inverse i (coeff1,h1,o1,divi1) in let newxh1 = Rhs.subst newxi i newxh1 in @@ -251,7 +251,7 @@ module EqualitiesConjunction = struct else (* var_i = var_i, i.e. it may occur on the rhs of other equalities *) (* so now, we transform with the inverse of the transformer: *) let inv = snd (inverse i (coeff,j,offs,divi)) in - IntMap.fold (fun k v acc -> + IntMap.fold (fun k v acc -> match v with | (Some (c,x),o,d) when x=i-> set_rhs acc k (Rhs.subst inv i v) | _ -> acc @@ -281,7 +281,7 @@ struct let multiply a b = (* if one of them is a constant, then multiply. Otherwise, the expression is not linear *) match a, b with - | [(None,coeff, divi)], c + | [(None,coeff, divi)], c | c, [(None,coeff, divi)] -> multiply_with_Q coeff divi c | _ -> raise NotLinearExpr in @@ -314,7 +314,7 @@ struct | x -> Some(x) (** convert and simplify (wrt. reference variables) a texpr into a tuple of a list of monomials (coeff,varidx,divi) and a (constant/divi) *) - let simplified_monomials_from_texp (t: t) texp = + let simplified_monomials_from_texp (t: t) texp = BatOption.bind (monomials_from_texp t texp) (fun monomiallist -> let d = Option.get t.d in @@ -323,7 +323,7 @@ struct | None -> let gcdee = Z.gcd adiv divi in exprcache,(Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi) | Some (coeff,idx) -> let (somevar,someoffs,somedivi)=Rhs.subst (EConj.get_rhs d idx) idx (v,offs,divi) in (* normalize! *) let newcache = Option.map_default (fun (coef,ter) -> IMap.add ter Q.((IMap.find_default zero ter exprcache) + make coef somedivi) exprcache) exprcache somevar in - let gcdee = Z.gcd adiv divi in + let gcdee = Z.gcd adiv divi in (newcache,(Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi)) in let (expr,constant) = List.fold_left accumulate_constants (IMap.empty,(Z.zero,Z.one)) monomiallist in (* abstract simplification of the guard wrt. reference variables *) @@ -339,7 +339,7 @@ struct BatOption.bind (simplified_monomials_from_texp t texp ) (fun (sum_of_terms, (constant,divisor)) -> (match sum_of_terms with - | [] -> Some (None, constant,divisor) + | [] -> Some (None, constant,divisor) | [(coeff,var,divi)] -> Some (Rhs.canonicalize (Some (Z.mul divisor coeff,var), Z.mul constant divi,Z.mul divisor divi)) |_ -> None)) @@ -447,7 +447,7 @@ struct let t1 = change_d t1 sup_env ~add:true ~del:false in let t2 = change_d t2 sup_env ~add:true ~del:false in match t1.d, t2.d with - | Some d1', Some d2' -> + | Some d1', Some d2' -> EConj.IntMap.fold (fun lhs rhs map -> meet_with_one_conj map lhs rhs) (snd d2') t1 (* even on sparse d2, this will chose the relevant conjs to meet with*) | _ -> {d = None; env = sup_env} @@ -489,7 +489,7 @@ struct - lhs itself - criteria A and B that characterize equivalence class, depending on the reference variable and the affine expression parameters wrt. each EConj - rhs1 - - rhs2 + - rhs2 however, we have to account for the sparseity of EConj maps by manually patching holes with default values *) let joinfunction lhs rhs1 rhs2 = ( @@ -516,15 +516,15 @@ struct let varentry ci offi ch offh xh = let (coeff,off,d) = Q.(ci,(offi*ch)-(ci*offh),ch) in (* compute new rhs in Q *) let (coeff,off,d) = Z.(coeff.num*d.den*off.den,off.num*d.den*coeff.den,d. num*coeff.den*off.den) in (* convert that back into Z *) - Rhs.canonicalize (Some(coeff,xh),off,d) + Rhs.canonicalize (Some(coeff,xh),off,d) in (* ci1 = a*ch1+b /\ ci2 = a*ch2+b *) (* ===> a = (ci1-ci2)/(ch1-ch2) b = ci2-a*ch2 *) - let constentry ci1 ci2 ch1 ch2 xh = + let constentry ci1 ci2 ch1 ch2 xh = let a = Q.((ci1-ci2) / (ch1-ch2)) in let b = Q.(ci2 - a*ch2) in Rhs.canonicalize (Some (Z.(a.num*b.den),xh),Z.(b.num*a.den) ,Z.(a.den*b.den) ) in - let iterate map l = + let iterate map l = match l with | (_, _, _, rhs , rhs' ) :: t when Rhs.equal rhs rhs' -> List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l | (h, _, _, ((Some (ch,_),oh,dh)), ((Some _,_,_) )) :: t -> List.fold (fun acc (i,_,_,(monom,oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make (fst@@Option.get monom) di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t @@ -630,8 +630,8 @@ struct let assign_exp ask t var exp no_ov = let res = assign_exp ask t var exp no_ov in - if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s" - (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; + if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %a \n exp: %a\n no_ov: %b -> \n %s" + (show t) Var.pretty var d_exp exp (Lazy.force no_ov) (show res); res let assign_var (t: VarManagement.t) v v' = @@ -640,7 +640,7 @@ struct let assign_var t v v' = let res = assign_var t v v' in - if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s" (show t) (Var.to_string v) (Var.to_string v') (show res) ; + if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %a \n v': %a\n -> %s" (show t) Var.pretty v Var.pretty v' (show res); res (** Parallel assignment of variables. @@ -693,7 +693,7 @@ struct let substitute_exp ask t var exp no_ov = let res = substitute_exp ask t var exp no_ov in - if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s" (show t) (Var.to_string var) d_exp exp (show res); + if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %a \n exp: %a \n -> \n %s" (show t) Var.pretty var d_exp exp (show res); res let substitute_exp ask t var exp no_ov = timing_wrap "substitution" (substitute_exp ask t var exp) no_ov diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 9627b4762a..1e0a223571 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -136,7 +136,7 @@ struct let expr = (** simplify asks for a constant value of some subexpression e, similar to a constant fold. In particular but not exclusively this query is answered by the 2 var equalities domain itself. This normalizes arbitrary expressions to a point where they - might be able to be represented by means of 2 var equalities + might be able to be represented by means of 2 var equalities This simplification happens during a time, when there are temporary variables a#in and a#out part of the expression, but are not represented in the ctx, thus queries may result in top for these variables. Wrapping this in speculative @@ -279,7 +279,7 @@ struct expr := BinOp(MinusA,!expr,prod,longlong) else expr := BinOp(PlusA,!expr,prod,longlong) - | None -> M.warn ~category:Analyzer "Invariant Apron: cannot convert to cil var: %s" (Var.to_string v); raise Unsupported_Linexpr1 + | None -> M.warn ~category:Analyzer "Invariant Apron: cannot convert to cil var: %a" Var.pretty v; raise Unsupported_Linexpr1 in Linexpr1.iter append_summand linexpr1; !expr From b735727192db12f77c2778fd2d21d2d1c431e3ce Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 17 Jul 2024 11:24:22 +0300 Subject: [PATCH 298/566] Add Printable functions to GobApron.Scalar --- src/cdomains/apron/gobApron.apron.ml | 13 +++++++++++++ src/cdomains/apron/sharedFunctions.apron.ml | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml index fca28ff8f0..80d32f1821 100644 --- a/src/cdomains/apron/gobApron.apron.ml +++ b/src/cdomains/apron/gobApron.apron.ml @@ -1,6 +1,19 @@ open Batteries include Apron +module Scalar = +struct + include Scalar + + let pp = print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) +end + module Coeff = struct include Coeff diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 1e0a223571..dc918be571 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -36,7 +36,7 @@ let int_of_scalar ?round (scalar: Scalar.t) = in Z_mlgmpidl.z_of_mpzf z | _ -> - failwith ("int_of_scalar: unsupported: " ^ Scalar.to_string scalar) + failwith ("int_of_scalar: unsupported: " ^ Scalar.show scalar) module type ConvertArg = @@ -263,7 +263,7 @@ struct else Const (CInt(i,ILongLong,None)), false else - (M.warn ~category:Analyzer "Invariant Apron: coefficient is not int: %s" (Scalar.to_string c); raise Unsupported_Linexpr1) + (M.warn ~category:Analyzer "Invariant Apron: coefficient is not int: %a" Scalar.pretty c; raise Unsupported_Linexpr1) | None -> raise Unsupported_Linexpr1) | _ -> raise Unsupported_Linexpr1 in From 8f0ebc506662946192a96faf75e97af9fdeabf70 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 17 Jul 2024 11:24:43 +0300 Subject: [PATCH 299/566] Use GobZ.pretty in lin2var tracing --- src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index c13aca60ea..65775d9188 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -361,7 +361,7 @@ struct else match simplify_to_ref_and_offset t (Texpr1.to_expr texpr) with | Some (None, offset, divisor) when Z.equal (Z.rem offset divisor) Z.zero -> let res = Z.div offset divisor in - (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string res) (IntOps.BigIntOps.to_string res); + (if M.tracing then M.tracel "bounds" "min: %a max: %a" GobZ.pretty res GobZ.pretty res; Some res, Some res) | _ -> None, None From 06ea5e050df7d03692912bbfc3022a068cf411f4 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 17 Jul 2024 13:24:46 +0200 Subject: [PATCH 300/566] Rm resolved TODO --- src/cdomains/apron/sharedFunctions.apron.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index a9406d48e9..8121635e89 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -275,7 +275,6 @@ struct let append_summand (c:Coeff.union_5) v = match V.to_cil_varinfo v with | Some vinfo when IntDomain.Size.is_cast_injective ~from_type:vinfo.vtype ~to_type:(TInt(ILongLong,[])) -> - (* TODO: What to do with variables that have a type that cannot be stored into ILongLong to avoid overflows? *) let var = Cilfacade.mkCast ~e:(Lval(Var vinfo,NoOffset)) ~newt:longlong in let coeff, flip = coeff_to_const true c in let prod = BinOp(Mult, coeff, var, longlong) in @@ -291,7 +290,7 @@ struct let lcm_den linexpr1 = - let exception UnsupportedScalar + let exception UnsupportedScalar in let frac_of_scalar scalar = if Scalar.is_infty scalar <> 0 then (* infinity means unbounded *) From 0f99b6652ada5e5c3863e80dcb6c8d746bcd4f3f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 18 Jul 2024 20:06:08 +0300 Subject: [PATCH 301/566] Vendor ppx_easy_deriving --- dune-project | 2 +- goblint.opam | 3 +- goblint.opam.locked | 6 - goblint.opam.template | 1 - src/ppx/lattice/dune | 1 - src/ppx/printable/dune | 1 - src/vendor/ppx_easy_deriving/README.md | 4 + src/vendor/ppx_easy_deriving/deriver.ml | 100 ++++++++ src/vendor/ppx_easy_deriving/deriver.mli | 3 + src/vendor/ppx_easy_deriving/deriver_intf.ml | 13 + src/vendor/ppx_easy_deriving/dune | 4 + src/vendor/ppx_easy_deriving/intf.ml | 64 +++++ src/vendor/ppx_easy_deriving/pat_exp.ml | 46 ++++ src/vendor/ppx_easy_deriving/pat_exp.mli | 16 ++ .../ppx_easy_deriving/ppx_easy_deriving.ml | 15 ++ src/vendor/ppx_easy_deriving/product.ml | 226 ++++++++++++++++++ src/vendor/ppx_easy_deriving/product.mli | 3 + src/vendor/ppx_easy_deriving/product_intf.ml | 132 ++++++++++ src/vendor/ppx_easy_deriving/util.ml | 12 + src/vendor/ppx_easy_deriving/util.mli | 8 + 20 files changed, 648 insertions(+), 12 deletions(-) create mode 100644 src/vendor/ppx_easy_deriving/README.md create mode 100644 src/vendor/ppx_easy_deriving/deriver.ml create mode 100644 src/vendor/ppx_easy_deriving/deriver.mli create mode 100644 src/vendor/ppx_easy_deriving/deriver_intf.ml create mode 100644 src/vendor/ppx_easy_deriving/dune create mode 100644 src/vendor/ppx_easy_deriving/intf.ml create mode 100644 src/vendor/ppx_easy_deriving/pat_exp.ml create mode 100644 src/vendor/ppx_easy_deriving/pat_exp.mli create mode 100644 src/vendor/ppx_easy_deriving/ppx_easy_deriving.ml create mode 100644 src/vendor/ppx_easy_deriving/product.ml create mode 100644 src/vendor/ppx_easy_deriving/product.mli create mode 100644 src/vendor/ppx_easy_deriving/product_intf.ml create mode 100644 src/vendor/ppx_easy_deriving/util.ml create mode 100644 src/vendor/ppx_easy_deriving/util.mli diff --git a/dune-project b/dune-project index 9071eb989f..97a06a220a 100644 --- a/dune-project +++ b/dune-project @@ -45,7 +45,7 @@ Goblint includes analyses for assertions, overflows, deadlocks, etc and can be e (ppx_deriving (>= 6.0.2)) (ppx_deriving_hash (>= 0.1.2)) (ppx_deriving_yojson (>= 3.7.0)) - ppx_easy_deriving + (ppxlib (>= 0.30.0)) ; ppx_easy_deriving (ounit2 :with-test) (qcheck-ounit :with-test) (odoc :with-doc) diff --git a/goblint.opam b/goblint.opam index c0b0b52960..78a8fb76e7 100644 --- a/goblint.opam +++ b/goblint.opam @@ -45,7 +45,7 @@ depends: [ "ppx_deriving" {>= "6.0.2"} "ppx_deriving_hash" {>= "0.1.2"} "ppx_deriving_yojson" {>= "3.7.0"} - "ppx_easy_deriving" + "ppxlib" {>= "0.30.0"} "ounit2" {with-test} "qcheck-ounit" {with-test} "odoc" {with-doc} @@ -98,7 +98,6 @@ available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] - [ "ppx_easy_deriving.~dev" "git+https://github.com/sim642/ppx_easy_deriving.git#3d599fdfb231e4a1f9bad0e914068210901533a4" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.locked b/goblint.opam.locked index b9540fa629..37f22c3230 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -58,7 +58,6 @@ depends: [ "dune-private-libs" {= "3.7.1"} "dune-site" {= "3.7.1"} "dyn" {= "3.7.1"} - "either" {= "1.0.0"} "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} @@ -86,7 +85,6 @@ depends: [ "ppx_deriving" {= "6.0.2"} "ppx_deriving_hash" {= "0.1.2"} "ppx_deriving_yojson" {= "3.7.0"} - "ppx_easy_deriving" {= "~dev"} "ppxlib" {= "0.32.1"} "qcheck-core" {= "0.20"} "qcheck-ounit" {= "0.20" & with-test} @@ -139,10 +137,6 @@ pin-depends: [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] - [ - "ppx_easy_deriving.~dev" - "git+https://github.com/sim642/ppx_easy_deriving.git#3d599fdfb231e4a1f9bad0e914068210901533a4" - ] ] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} description: """\ diff --git a/goblint.opam.template b/goblint.opam.template index e9b53c42b4..a730d5c064 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -4,7 +4,6 @@ available: os-distribution != "alpine" & arch != "arm64" pin-depends: [ # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] - [ "ppx_easy_deriving.~dev" "git+https://github.com/sim642/ppx_easy_deriving.git#3d599fdfb231e4a1f9bad0e914068210901533a4" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/src/ppx/lattice/dune b/src/ppx/lattice/dune index 4c057c6fab..1f4dac4b82 100644 --- a/src/ppx/lattice/dune +++ b/src/ppx/lattice/dune @@ -4,5 +4,4 @@ (name ppx_deriving_lattice) (kind ppx_deriver) (libraries ppxlib ppx_easy_deriving) - (ppx_runtime_libraries ppx_easy_deriving.runtime) (preprocess (pps ppxlib.metaquot))) diff --git a/src/ppx/printable/dune b/src/ppx/printable/dune index 2b620d2319..9164e487e0 100644 --- a/src/ppx/printable/dune +++ b/src/ppx/printable/dune @@ -4,5 +4,4 @@ (name ppx_deriving_printable) (kind ppx_deriver) (libraries ppxlib ppx_easy_deriving) - (ppx_runtime_libraries ppx_easy_deriving.runtime) (preprocess (pps ppxlib.metaquot))) diff --git a/src/vendor/ppx_easy_deriving/README.md b/src/vendor/ppx_easy_deriving/README.md new file mode 100644 index 0000000000..99a5e7e140 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/README.md @@ -0,0 +1,4 @@ +# ppx_easy_deriving + +Goblint vendors a subset of the unreleased [ppx_easy_deriving](https://github.com/sim642/ppx_easy_deriving) library. +It only includes products, excluding simples and variants. diff --git a/src/vendor/ppx_easy_deriving/deriver.ml b/src/vendor/ppx_easy_deriving/deriver.ml new file mode 100644 index 0000000000..533a77959c --- /dev/null +++ b/src/vendor/ppx_easy_deriving/deriver.ml @@ -0,0 +1,100 @@ +open Ppxlib +open Ast_builder.Default + +include Deriver_intf + +module Make (Arg: Intf.S): S = +struct + let attr = Attribute.declare (Printf.sprintf "deriving.%s.%s" Arg.name Arg.name) Attribute.Context.core_type Ast_pattern.(single_expr_payload __) (fun expr -> expr) + + let unit ~loc = Arg.tuple ~loc [] + + let rec expr ~loc ~quoter ct = + match Attribute.get attr ct with + | Some expr -> + Expansion_helpers.Quoter.quote quoter expr + | None -> + match ct with + | [%type: unit] -> + unit ~loc + | {ptyp_desc = Ptyp_constr ({txt = lid; loc}, args); _} -> + let ident = pexp_ident ~loc {loc; txt = Expansion_helpers.mangle_lid (Prefix Arg.name) lid} in + let ident = Expansion_helpers.Quoter.quote quoter ident in + let apply_args = List.map (fun ct -> + (Nolabel, expr ~loc ~quoter ct) + ) args + in + pexp_apply ~loc ident apply_args + | {ptyp_desc = Ptyp_tuple elems; _} -> + expr_tuple ~loc ~quoter elems + | {ptyp_desc = Ptyp_var name; _} -> + evar ~loc ("poly_" ^ name) + | _ -> + pexp_extension ~loc (Location.error_extensionf ~loc "unsupported core type") + + and expr_record ~loc ~quoter (lds: label_declaration list) = + let les = List.map (fun {pld_name = {txt = label; _}; pld_type; _} -> + (Lident label, expr ~loc ~quoter pld_type) + ) lds + in + Arg.record ~loc les + + and expr_tuple ~loc ~quoter elems = + let es = List.map (expr ~loc ~quoter) elems in + Arg.tuple ~loc es + + let expr_declaration ~loc ~quoter = function + | {ptype_kind = Ptype_abstract; ptype_manifest = Some ct; _} -> + expr ~loc ~quoter ct + | {ptype_kind = Ptype_abstract; _} -> + pexp_extension ~loc (Location.error_extensionf ~loc "unsupported abstract type") + | {ptype_kind = Ptype_variant constrs; _} -> + pexp_extension ~loc (Location.error_extensionf ~loc "unsupported variant type") + | {ptype_kind = Ptype_open; _} -> + pexp_extension ~loc (Location.error_extensionf ~loc "unsupported open type") + | {ptype_kind = Ptype_record fields; _} -> + expr_record ~loc ~quoter fields + + let typ ~loc td = + let ct = Ppx_deriving.core_type_of_type_decl td in + Ppx_deriving.poly_arrow_of_type_decl + (Arg.typ ~loc) + td + (Arg.typ ~loc ct) + + let generate_impl ~ctxt (_rec_flag, type_declarations) = + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + let vbs = List.map (fun td -> + let quoter = Expansion_helpers.Quoter.create () in + let expr = expr_declaration ~loc ~quoter td in + let expr = Expansion_helpers.Quoter.sanitize quoter expr in + let expr = Ppx_deriving.poly_fun_of_type_decl td expr in + let ct = typ ~loc td in + let pat = ppat_var ~loc {loc; txt = Expansion_helpers.mangle_type_decl (Prefix Arg.name) td} in + let pat = ppat_constraint ~loc pat ct in + Ast_helper.Vb.mk ~loc ~attrs:[Ppx_deriving.attr_warning [%expr "-39"]] pat expr + ) type_declarations + in + [Ast_helper.Str.value ~loc Recursive vbs] + + let generate_intf ~ctxt (_rec_flag, type_declarations) = + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + List.map (fun td -> + let ct = typ ~loc td in + let val_ = Ast_helper.Val.mk ~loc {loc; txt = Expansion_helpers.mangle_type_decl (Prefix Arg.name) td} ct in + Ast_helper.Sig.value ~loc val_ + ) type_declarations + + let impl_generator = Deriving.Generator.V2.make_noarg generate_impl + let intf_generator = Deriving.Generator.V2.make_noarg generate_intf + let extension ~loc ~path:_ ct = + let quoter = Expansion_helpers.Quoter.create () in + let expr = expr ~loc ~quoter ct in + Expansion_helpers.Quoter.sanitize quoter expr + + let register () = + Deriving.add Arg.name + ~sig_type_decl:intf_generator + ~str_type_decl:impl_generator + ~extension +end diff --git a/src/vendor/ppx_easy_deriving/deriver.mli b/src/vendor/ppx_easy_deriving/deriver.mli new file mode 100644 index 0000000000..a8238f6f6f --- /dev/null +++ b/src/vendor/ppx_easy_deriving/deriver.mli @@ -0,0 +1,3 @@ +(** Registerable deriver. *) + +include Deriver_intf.Deriver (** @inline *) diff --git a/src/vendor/ppx_easy_deriving/deriver_intf.ml b/src/vendor/ppx_easy_deriving/deriver_intf.ml new file mode 100644 index 0000000000..565a6aef38 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/deriver_intf.ml @@ -0,0 +1,13 @@ +module type S = +sig + val register: unit -> Ppxlib.Deriving.t + (** Register deriver with ppxlb. *) +end + +module type Deriver = +sig + module type S = S + + module Make (_: Intf.S): S + (** Make registerable deriver. *) +end diff --git a/src/vendor/ppx_easy_deriving/dune b/src/vendor/ppx_easy_deriving/dune new file mode 100644 index 0000000000..7d50a56e43 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/dune @@ -0,0 +1,4 @@ +(library + (name ppx_easy_deriving) + (libraries ppxlib ppx_deriving.api) + (preprocess (pps ppxlib.metaquot))) diff --git a/src/vendor/ppx_easy_deriving/intf.ml b/src/vendor/ppx_easy_deriving/intf.ml new file mode 100644 index 0000000000..b30c7c36a0 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/intf.ml @@ -0,0 +1,64 @@ +(** Main interfaces. *) + +open Ppxlib + +(** Deriver name interface. *) +module type Name = +sig + val name: string + (** Deriver name. + + For example, with the name "equal": + + Use [[@@deriving equal]] after a type definition. + + The derived value/function is [val equal: ...] (if the type is named [t]) or [val equal_ty: ...] (otherwise if the type is named [ty]). + + Use [[@equal ...]] after a type expression to override the underlying value/function used for it. + + Use [[%equal: ty]] as an expression for the value/function of type [ty]. *) +end + +(** Deriver base interface. *) +module type Base = +sig + include Name + val typ: loc:location -> core_type -> core_type + (** Derived value/function type for a given type. + + For example, "equal" deriver would map [t] to [t -> t -> bool]. *) +end + +module Tuple = +struct + + (** Tuple deriver interface. *) + module type S = + sig + include Base + val tuple: loc:location -> expression list -> expression + (** Compose derived values/functions for tuple elements into derived value/function for the tuple. *) + end +end + +module Record = +struct + + (** Record deriver interface. *) + module type S = + sig + include Base + val record: loc:location -> (longident * expression) list -> expression + (** Compose derived values/functions for record fields into derived value/function for the record. *) + end +end + +module Full = +struct + + (** Full deriver interface. *) + module type S = + sig + include Tuple.S + include Record.S + end +end + +module type S = Full.S +(** Deriver interface. *) diff --git a/src/vendor/ppx_easy_deriving/pat_exp.ml b/src/vendor/ppx_easy_deriving/pat_exp.ml new file mode 100644 index 0000000000..a9d43d07e6 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/pat_exp.ml @@ -0,0 +1,46 @@ +open Ppxlib +open Ast_builder.Default + +type t = + | Record of (longident * t) list + | Tuple of t list + | Unit + | Base of string +let create_record ~prefix ls = + Record (List.mapi (fun i l -> (l, Base (prefix ^ string_of_int (i + 1)))) ls) +let create_tuple ~prefix n = + match n with + | 0 -> Unit + | 1 -> Base (prefix ^ "1") + | n -> Tuple (List.init n (fun i -> Base (prefix ^ string_of_int (i + 1)))) +let rec to_pat ~loc = function + | Record xs -> + ppat_record ~loc (List.map (fun (l, x) -> + (Located.mk ~loc l, to_pat ~loc x) + ) xs) Closed + | Tuple xs -> + ppat_tuple ~loc (List.map (to_pat ~loc) xs) + | Unit -> + [%pat? ()] + | Base s -> + ppat_var ~loc (Located.mk ~loc s) +let rec to_exps ~loc = function + | Record xs -> + List.flatten (List.map (fun (_, x) -> to_exps ~loc x) xs) + | Tuple xs -> + List.flatten (List.map (to_exps ~loc) xs) + | Unit -> + [] + | Base s -> + [pexp_ident ~loc {loc; txt = Lident s}] +let rec to_exp ~loc = function + | Record xs -> + pexp_record ~loc (List.map (fun (l, x) -> + (Located.mk ~loc l, to_exp ~loc x) + ) xs) None + | Tuple xs -> + pexp_tuple ~loc (List.map (to_exp ~loc) xs) + | Unit -> + [%expr ()] + | Base s -> + pexp_ident ~loc {loc; txt = Lident s} diff --git a/src/vendor/ppx_easy_deriving/pat_exp.mli b/src/vendor/ppx_easy_deriving/pat_exp.mli new file mode 100644 index 0000000000..a4878afa81 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/pat_exp.mli @@ -0,0 +1,16 @@ +(** Common representation of patterns and expressions. *) + +open Ppxlib + +type t = + | Record of (longident * t) list + | Tuple of t list + | Unit + | Base of string + +val create_record: prefix:string -> longident list -> t +val create_tuple: prefix:string -> int -> t + +val to_pat: loc:location -> t -> pattern +val to_exps: loc:location -> t -> expression list +val to_exp: loc:location -> t -> expression diff --git a/src/vendor/ppx_easy_deriving/ppx_easy_deriving.ml b/src/vendor/ppx_easy_deriving/ppx_easy_deriving.ml new file mode 100644 index 0000000000..a490f33557 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/ppx_easy_deriving.ml @@ -0,0 +1,15 @@ +(** Library for easily defining PPX derivers without boilerplate and runtime overhead. *) + +(** {1 Interfaces} *) + +include Intf (** @inline *) + + +(** {1 Deriver} *) + +module Deriver = Deriver + + +(** {1 Easier constructs} *) + +module Product = Product diff --git a/src/vendor/ppx_easy_deriving/product.ml b/src/vendor/ppx_easy_deriving/product.ml new file mode 100644 index 0000000000..2135552b63 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/product.ml @@ -0,0 +1,226 @@ +open Ppxlib +open Ast_builder.Default + +include Product_intf + +module type Product_S = S + +module Make (P: S): Intf.S = +struct + include P + + let tuple ~loc es = + let n = List.length es in + let pe_create = Pat_exp.create_tuple n in + P.product ~loc ~pe_create es + + let record ~loc les = + let ls = List.map fst les in + let pe_create = Pat_exp.create_record ls in + let es = List.map snd les in + P.product ~loc ~pe_create es +end + +module Reduce = +struct + include Reduce + + module Conjunctive = + struct + include Conjunctive + + module Make (C: S): Reduce.S = + struct + let name = C.name + let typ ~loc _ = [%type: bool] + let unit ~loc = [%expr true] + let both ~loc e1 e2 = [%expr [%e e1] && [%e e2]] + end + end +end + +module Reduce1 = +struct + include Reduce1 + + module Make (R1: S): Intf.S = + struct + module P: Product_S = + struct + let name = R1.name + let typ ~loc t = [%type: [%t t] -> [%t R1.typ ~loc t]] + + let product ~loc ~pe_create es = + let pe = pe_create ~prefix:"x" in + let body = + let es = List.map2 (fun e x -> + [%expr [%e e] [%e x]] + ) es (Pat_exp.to_exps ~loc pe) + in + Util.reduce ~unit:(R1.unit ~loc) ~both:(R1.both ~loc) es + in + [%expr fun [%p Pat_exp.to_pat ~loc pe] -> [%e body]] + end + + include Make (P) + end +end + +module Reduce2 = +struct + include Reduce2 + + module Make (R2: S): Intf.S = + struct + module P: Product_S = + struct + let name = R2.name + let typ ~loc t = [%type: [%t t] -> [%t t] -> [%t R2.typ ~loc t]] + + let product ~loc ~pe_create es = + let pel = pe_create ~prefix:"l" in + let per = pe_create ~prefix:"r" in + let body = + let esl = Pat_exp.to_exps ~loc pel in + let esr = Pat_exp.to_exps ~loc per in + let es = Util.map3 (fun e l r -> + [%expr [%e e] [%e l] [%e r]] + ) es esl esr + in + Util.reduce ~unit:(R2.unit ~loc) ~both:(R2.both ~loc) es + in + let pl = Pat_exp.to_pat ~loc pel in + let pr = Pat_exp.to_pat ~loc per in + [%expr fun [%p pl] [%p pr] -> [%e body]] + end + + include Make (P) + end +end + +module Create = +struct + include Create + + module Make (C: S): Intf.S = + struct + let name = C.name + let typ ~loc t = [%type: [%t C.typ ~loc t] -> [%t t]] + + let tuple ~loc es = + match es with + | [] -> [%expr fun _ -> ()] + | [e] -> e + | _ :: _ -> + let elems = List.map (fun e -> + [%expr [%e e] x] + ) es + in + let body = pexp_tuple ~loc elems in + [%expr fun x -> [%e body]] + + let record ~loc les = + let fields = List.map (fun (l, e) -> + (Located.mk ~loc l, [%expr [%e e] x]) + ) les + in + let body = pexp_record ~loc fields None in + [%expr fun x -> [%e body]] + end +end + +module Map1 = +struct + include Map1 + + module Make (M1: S): Intf.S = + struct + let name = M1.name + let typ ~loc t = [%type: [%t t] -> [%t t]] + + let tuple ~loc es = + let n = List.length es in + let pe = Pat_exp.create_tuple ~prefix:"x" n in + let elems = + List.map2 (fun e x -> + [%expr [%e e] [%e x]] + ) es (Pat_exp.to_exps ~loc pe) + in + let body = + match elems with + | [] -> [%expr ()] + | [elem] -> elem + | _ :: _ -> pexp_tuple ~loc elems + in + [%expr fun [%p Pat_exp.to_pat ~loc pe] -> [%e body]] + + let record ~loc les = + let ls = List.map fst les in + let pe = Pat_exp.create_record ~prefix:"x" ls in + let es = List.map snd les in + let elems = + List.map2 (fun e x -> + [%expr [%e e] [%e x]] + ) es (Pat_exp.to_exps ~loc pe) + in + let fields = List.map2 (fun l elem -> + (Located.mk ~loc l, elem) + ) ls elems + in + let body = pexp_record ~loc fields None in + [%expr fun [%p Pat_exp.to_pat ~loc pe] -> [%e body]] + end +end + +module Map2 = +struct + include Map2 + + module Make (M2: S): Intf.S = + struct + let name = M2.name + let typ ~loc t = [%type: [%t t] -> [%t t] -> [%t t]] + + let tuple ~loc es = + let n = List.length es in + let pel = Pat_exp.create_tuple ~prefix:"l" n in + let per = Pat_exp.create_tuple ~prefix:"r" n in + let elems = + let esl = Pat_exp.to_exps ~loc pel in + let esr = Pat_exp.to_exps ~loc per in + Util.map3 (fun e l r -> + [%expr [%e e] [%e l] [%e r]] + ) es esl esr + in + let body = + match elems with + | [] -> [%expr ()] + | [elem] -> elem + | _ :: _ -> pexp_tuple ~loc elems + in + let pl = Pat_exp.to_pat ~loc pel in + let pr = Pat_exp.to_pat ~loc per in + [%expr fun [%p pl] [%p pr] -> [%e body]] + + let record ~loc les = + let ls = List.map fst les in + let pel = Pat_exp.create_record ~prefix:"l" ls in + let per = Pat_exp.create_record ~prefix:"r" ls in + let es = List.map snd les in + let elems = + let esl = Pat_exp.to_exps ~loc pel in + let esr = Pat_exp.to_exps ~loc per in + Util.map3 (fun e l r -> + [%expr [%e e] [%e l] [%e r]] + ) es esl esr + in + let fields = List.map2 (fun l elem -> + (Located.mk ~loc l, elem) + ) ls elems + in + let body = pexp_record ~loc fields None in + let pl = Pat_exp.to_pat ~loc pel in + let pr = Pat_exp.to_pat ~loc per in + [%expr fun [%p pl] [%p pr] -> [%e body]] + end +end diff --git a/src/vendor/ppx_easy_deriving/product.mli b/src/vendor/ppx_easy_deriving/product.mli new file mode 100644 index 0000000000..d0b45f106c --- /dev/null +++ b/src/vendor/ppx_easy_deriving/product.mli @@ -0,0 +1,3 @@ +(** Product derivers which unify tuple and record derivers. *) + +include Product_intf.Product (** @inline *) diff --git a/src/vendor/ppx_easy_deriving/product_intf.ml b/src/vendor/ppx_easy_deriving/product_intf.ml new file mode 100644 index 0000000000..14964bbee7 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/product_intf.ml @@ -0,0 +1,132 @@ +open Ppxlib + +module type S = +sig + include Intf.Base + val product: loc:location -> pe_create:(prefix:string -> Pat_exp.t) -> expression list -> expression + (** Compose derived values/functions for product elements into derived value/function for the product. + + @param pe_create factory for patterns/expressions of the actual type. *) +end + +module Reduce = +struct + module type S = + sig + include Intf.Base + val unit: loc:location -> expression + (** Derived value/function for [unit] type. *) + + val both: loc:location -> expression -> expression -> expression + (** Compose derived values/functions in a product into derived value/function for the pair. *) + end + + module Conjunctive = + struct + module type S = Intf.Name + end +end + +module Reduce1 = +struct + module type S = Reduce.S +end + +module Reduce2 = +struct + module type S = Reduce.S +end + +module Create = +struct + module type S = + sig + include Intf.Base + end +end + +module Map1 = +struct + module type S = Intf.Name +end + +module Map2 = +struct + module type S = Intf.Name +end + +module type Product = +sig + module type S = S + (** Product deriver interface. *) + + module Make (P: S): Intf.S + (** Make deriver from product deriver. *) + + (** Reductions for reducing derivers. *) + module Reduce : + sig + module type S = Reduce.S + (** Reduction interface. *) + + (** Conjunctive reduction. *) + module Conjunctive : + sig + module type S = Reduce.Conjunctive.S + (** Conjunctive reduction interface. *) + + module Make (C: S): Reduce.S + (** Make reduction from conjunctive reduction. *) + end + end + + (** Unary reducing deriver. *) + module Reduce1 : + sig + module type S = Reduce1.S + (** Unary reducing deriver interface. *) + + module Make (R1: S): Intf.S + (** Make deriver from unary reducing deriver. *) + end + + (** Binary reducing deriver. *) + module Reduce2 : + sig + module type S = Reduce2.S + (** Binary reducing deriver interface. *) + + module Make (R2: S): Intf.S + (** Make deriver from binary reducing deriver. *) + end + + (** Unary creating deriver. *) + module Create : + sig + module type S = Create.S + (** Unary creating deriver interface. *) + + module Make (C: S): Intf.S + (** Make deriver from unary creating deriver. *) + end + + (** Unary mapping deriver. *) + module Map1 : + sig + module type S = Map1.S + (** Unary mapping deriver interface. *) + + module Make (M1: S): Intf.S + (** Make deriver from unary mapping deriver. *) + end + + (** Binary mapping deriver. *) + module Map2 : + sig + module type S = Map2.S + (** Binary mapping deriver interface. *) + + module Make (M2: S): Intf.S + (** Make deriver from binary mapping deriver. *) + end +end diff --git a/src/vendor/ppx_easy_deriving/util.ml b/src/vendor/ppx_easy_deriving/util.ml new file mode 100644 index 0000000000..fbb9d3f642 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/util.ml @@ -0,0 +1,12 @@ +let reduce ~unit ~both = function + | [] -> unit + | [x] -> x + | xs -> + let xs = List.rev xs in + match xs with + | x :: xs -> + List.fold_right both (List.rev xs) x (* omits hash_empty *) + | [] -> assert false + +let map3 f l1 l2 l3 = + List.map2 (fun x1 (x2, x3) -> f x1 x2 x3) l1 (List.combine l2 l3) (* TODO: optimize *) diff --git a/src/vendor/ppx_easy_deriving/util.mli b/src/vendor/ppx_easy_deriving/util.mli new file mode 100644 index 0000000000..0f896e7efc --- /dev/null +++ b/src/vendor/ppx_easy_deriving/util.mli @@ -0,0 +1,8 @@ +(** Utility functions. *) + +val reduce: unit:'a -> both:('a -> 'a -> 'a) -> 'a list -> 'a +(** Reduce a list of values "optimally", + i.e. without unnecessary [~unit] and [~both]. *) + +val map3: ('a -> 'b -> 'c -> 'd) -> 'a list -> 'b list -> 'c list -> 'd list +(** Map three lists into one. *) From a39ab19882e9fc27fc1ba2e9e75ca290bda07e49 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 18 Jul 2024 20:12:24 +0300 Subject: [PATCH 302/566] Deduplicate (include_subdirs no) in src/ppx --- src/ppx/dune | 1 + src/ppx/lattice/dune | 2 -- src/ppx/printable/dune | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) create mode 100644 src/ppx/dune diff --git a/src/ppx/dune b/src/ppx/dune new file mode 100644 index 0000000000..ff757cb8ca --- /dev/null +++ b/src/ppx/dune @@ -0,0 +1 @@ +(include_subdirs no) diff --git a/src/ppx/lattice/dune b/src/ppx/lattice/dune index 1f4dac4b82..862298be13 100644 --- a/src/ppx/lattice/dune +++ b/src/ppx/lattice/dune @@ -1,5 +1,3 @@ -(include_subdirs no) - (library (name ppx_deriving_lattice) (kind ppx_deriver) diff --git a/src/ppx/printable/dune b/src/ppx/printable/dune index 9164e487e0..8e08232de6 100644 --- a/src/ppx/printable/dune +++ b/src/ppx/printable/dune @@ -1,5 +1,3 @@ -(include_subdirs no) - (library (name ppx_deriving_printable) (kind ppx_deriver) From cd793b9e7293bd94920a09b7388e5cc19bf8518c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 18 Jul 2024 20:17:00 +0300 Subject: [PATCH 303/566] Exclude PPX-s from Goblint_lib documentation check --- scripts/goblint-lib-modules.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 017530f838..ba25a1403c 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -42,6 +42,10 @@ "Goblint_build_info", "Dune_build_info", + # ppx-s + "Ppx_deriving_printable", + "Ppx_deriving_lattice", + "MessageCategory", # included in Messages "PreValueDomain", # included in ValueDomain From 2e1d5fec8e9a8c955b0a86fdd22a63a2d0b05ec3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 18 Jul 2024 20:58:27 +0300 Subject: [PATCH 304/566] Add LICENSE for vendored ppx_easy_deriving --- src/vendor/ppx_easy_deriving/LICENSE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/vendor/ppx_easy_deriving/LICENSE.md diff --git a/src/vendor/ppx_easy_deriving/LICENSE.md b/src/vendor/ppx_easy_deriving/LICENSE.md new file mode 100644 index 0000000000..ae6337b11b --- /dev/null +++ b/src/vendor/ppx_easy_deriving/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Simmo Saan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 50b7e145cfb81c3c72e5d3b22a9df4ac74fecaa9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Jul 2024 13:57:07 +0300 Subject: [PATCH 305/566] Add yamlWitnessStripDiff for cram test diff binary gives differently biased output on OSX, this should be fully deterministic. --- tests/regression/36-apron/52-queuesize.t | 53 +++++++++-------- tests/regression/dune | 6 +- tests/util/dune | 4 +- tests/util/yamlWitnessStrip.ml | 65 +------------------- tests/util/yamlWitnessStripCommon.ml | 75 ++++++++++++++++++++++++ tests/util/yamlWitnessStripDiff.ml | 29 +++++++++ 6 files changed, 139 insertions(+), 93 deletions(-) create mode 100644 tests/util/yamlWitnessStripCommon.ml create mode 100644 tests/util/yamlWitnessStripDiff.ml diff --git a/tests/regression/36-apron/52-queuesize.t b/tests/regression/36-apron/52-queuesize.t index 987fe53f77..62851f2ec9 100644 --- a/tests/regression/36-apron/52-queuesize.t +++ b/tests/regression/36-apron/52-queuesize.t @@ -252,29 +252,30 @@ With diff-box: Compare witnesses: - $ diff witness-disable-diff-box.yml witness-enable-diff-box.yml - 9,19d8 - < string: 2147483647LL - (long long )capacity >= 0LL - < type: assertion - < format: C - < - entry_type: location_invariant - < location: - < file_name: 52-queuesize.c - < file_hash: $FILE_HASH - < line: 36 - < column: 3 - < function: push - < location_invariant: - 44,54d32 - < type: assertion - < format: C - < - entry_type: location_invariant - < location: - < file_name: 52-queuesize.c - < file_hash: $FILE_HASH - < line: 15 - < column: 3 - < function: pop - < location_invariant: - < string: 2147483647LL - (long long )capacity >= 0LL - [1] + $ yamlWitnessStripDiff witness-disable-diff-box.yml witness-enable-diff-box.yml + # Left-only entries: + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: push + location_invariant: + string: 2147483647LL - (long long )capacity >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 15 + column: 3 + function: pop + location_invariant: + string: 2147483647LL - (long long )capacity >= 0LL + type: assertion + format: C + --- + # Right-only entries: + [] diff --git a/tests/regression/dune b/tests/regression/dune index 8f550d190e..0fd8052478 100644 --- a/tests/regression/dune +++ b/tests/regression/dune @@ -2,7 +2,8 @@ (_ (binaries ./cfg/util/cfgDot.exe - ../util/yamlWitnessStrip.exe))) + ../util/yamlWitnessStrip.exe + ../util/yamlWitnessStripDiff.exe))) (rule (alias runtest) @@ -24,4 +25,5 @@ (alias runcramtest) (deps (package goblint) - %{bin:yamlWitnessStrip})) + %{bin:yamlWitnessStrip} + %{bin:yamlWitnessStripDiff})) diff --git a/tests/util/dune b/tests/util/dune index 6637217651..0e32304d4f 100644 --- a/tests/util/dune +++ b/tests/util/dune @@ -1,5 +1,5 @@ -(executable - (name yamlWitnessStrip) +(executables + (names yamlWitnessStrip yamlWitnessStripDiff) (libraries batteries.unthreaded goblint_std diff --git a/tests/util/yamlWitnessStrip.ml b/tests/util/yamlWitnessStrip.ml index 8a5046d6ff..ece604f9a8 100644 --- a/tests/util/yamlWitnessStrip.ml +++ b/tests/util/yamlWitnessStrip.ml @@ -1,55 +1,5 @@ open Goblint_lib -open YamlWitnessType - -module StrippedEntry = -struct - type t = { - entry_type: EntryType.t; - } - [@@deriving ord] - - let strip_file_hashes {entry_type} = - let stripped_file_hash = "$FILE_HASH" in - let location_strip_file_hash location: Location.t = - {location with file_hash = stripped_file_hash} - in - let target_strip_file_hash target: Target.t = - {target with file_hash = stripped_file_hash} - in - let invariant_strip_file_hash ({invariant_type}: InvariantSet.Invariant.t): InvariantSet.Invariant.t = - let invariant_type: InvariantSet.InvariantType.t = - match invariant_type with - | LocationInvariant x -> - LocationInvariant {x with location = location_strip_file_hash x.location} - | LoopInvariant x -> - LoopInvariant {x with location = location_strip_file_hash x.location} - in - {invariant_type} - in - let entry_type: EntryType.t = - match entry_type with - | LocationInvariant x -> - LocationInvariant {x with location = location_strip_file_hash x.location} - | LoopInvariant x -> - LoopInvariant {x with location = location_strip_file_hash x.location} - | FlowInsensitiveInvariant x -> - FlowInsensitiveInvariant x (* no location to strip *) - | PreconditionLoopInvariant x -> - PreconditionLoopInvariant {x with location = location_strip_file_hash x.location} - | LoopInvariantCertificate x -> - LoopInvariantCertificate {x with target = target_strip_file_hash x.target} - | PreconditionLoopInvariantCertificate x -> - PreconditionLoopInvariantCertificate {x with target = target_strip_file_hash x.target} - | InvariantSet x -> - InvariantSet {content = List.map invariant_strip_file_hash x.content} - in - {entry_type} - - let to_yaml {entry_type} = - `O ([ - ("entry_type", `String (EntryType.entry_type entry_type)); - ] @ EntryType.to_yaml' entry_type) -end +open YamlWitnessStripCommon (* Use set for output, so order is deterministic regardless of Goblint. *) module StrippedEntrySet = Set.Make (StrippedEntry) @@ -69,18 +19,7 @@ let main () = stripped_entries ) StrippedEntrySet.empty yaml_entries in - let stripped_yaml_entries = - StrippedEntrySet.elements stripped_entries - |> List.map StrippedEntry.strip_file_hashes - |> List.rev_map StrippedEntry.to_yaml - in - let stripped_yaml = `A stripped_yaml_entries in - (* to_file/to_string uses a fixed-size buffer... *) - let stripped_yaml_str = match GobYaml.to_string' stripped_yaml with - | Ok text -> text - | Error (`Msg m) -> failwith ("Yaml.to_string: " ^ m) - in - Batteries.output_string Batteries.stdout stripped_yaml_str + print_stripped_entries stripped_entries let () = main () diff --git a/tests/util/yamlWitnessStripCommon.ml b/tests/util/yamlWitnessStripCommon.ml new file mode 100644 index 0000000000..e2c6905f02 --- /dev/null +++ b/tests/util/yamlWitnessStripCommon.ml @@ -0,0 +1,75 @@ +open Goblint_lib +open YamlWitnessType + +module StrippedEntry = +struct + type t = { + entry_type: EntryType.t; + } + [@@deriving ord] + + let strip_file_hashes {entry_type} = + let stripped_file_hash = "$FILE_HASH" in + let location_strip_file_hash location: Location.t = + {location with file_hash = stripped_file_hash} + in + let target_strip_file_hash target: Target.t = + {target with file_hash = stripped_file_hash} + in + let invariant_strip_file_hash ({invariant_type}: InvariantSet.Invariant.t): InvariantSet.Invariant.t = + let invariant_type: InvariantSet.InvariantType.t = + match invariant_type with + | LocationInvariant x -> + LocationInvariant {x with location = location_strip_file_hash x.location} + | LoopInvariant x -> + LoopInvariant {x with location = location_strip_file_hash x.location} + in + {invariant_type} + in + let entry_type: EntryType.t = + match entry_type with + | LocationInvariant x -> + LocationInvariant {x with location = location_strip_file_hash x.location} + | LoopInvariant x -> + LoopInvariant {x with location = location_strip_file_hash x.location} + | FlowInsensitiveInvariant x -> + FlowInsensitiveInvariant x (* no location to strip *) + | PreconditionLoopInvariant x -> + PreconditionLoopInvariant {x with location = location_strip_file_hash x.location} + | LoopInvariantCertificate x -> + LoopInvariantCertificate {x with target = target_strip_file_hash x.target} + | PreconditionLoopInvariantCertificate x -> + PreconditionLoopInvariantCertificate {x with target = target_strip_file_hash x.target} + | InvariantSet x -> + InvariantSet {content = List.map invariant_strip_file_hash x.content} + in + {entry_type} + + let to_yaml {entry_type} = + `O ([ + ("entry_type", `String (EntryType.entry_type entry_type)); + ] @ EntryType.to_yaml' entry_type) + + let of_yaml y = + let open GobYaml in + let+ entry_type = y |> EntryType.of_yaml in + {entry_type} +end + +(* Use set for output, so order is deterministic regardless of Goblint. *) +module StrippedEntrySet = Set.Make (StrippedEntry) + +let print_stripped_entries stripped_entries = + let stripped_yaml_entries = + StrippedEntrySet.elements stripped_entries + |> List.map StrippedEntry.strip_file_hashes + |> List.rev_map StrippedEntry.to_yaml + in + + let stripped_yaml = `A stripped_yaml_entries in + (* to_file/to_string uses a fixed-size buffer... *) + let stripped_yaml_str = match GobYaml.to_string' stripped_yaml with + | Ok text -> text + | Error (`Msg m) -> failwith ("Yaml.to_string: " ^ m) + in + Batteries.output_string Batteries.stdout stripped_yaml_str diff --git a/tests/util/yamlWitnessStripDiff.ml b/tests/util/yamlWitnessStripDiff.ml new file mode 100644 index 0000000000..8a993382b3 --- /dev/null +++ b/tests/util/yamlWitnessStripDiff.ml @@ -0,0 +1,29 @@ +open YamlWitnessStripCommon + +let read_stripped_entries path = + let yaml_str = BatFile.with_file_in path BatIO.read_all in + let yaml = Yaml.of_string_exn yaml_str in + let yaml_entries = yaml |> GobYaml.list |> BatResult.get_ok in + + List.fold_left (fun stripped_entries yaml_entry -> + match StrippedEntry.of_yaml yaml_entry with + | Ok stripped_entry -> + StrippedEntrySet.add stripped_entry stripped_entries + | Error (`Msg e) -> + Format.eprintf "couldn't parse entry: %s" e; + stripped_entries + ) StrippedEntrySet.empty yaml_entries + +let main () = + let left_stripped_entries = read_stripped_entries Sys.argv.(1) in + let right_stripped_entries = read_stripped_entries Sys.argv.(2) in + let left_only_stripped_entries = StrippedEntrySet.diff left_stripped_entries right_stripped_entries in + let right_only_stripped_entries = StrippedEntrySet.diff right_stripped_entries left_stripped_entries in + + print_endline "# Left-only entries:"; + print_stripped_entries left_only_stripped_entries; + print_endline "---"; + print_endline "# Right-only entries:"; + print_stripped_entries right_only_stripped_entries + +let () = main () From 65d4c02128083cb42ce2a51d3f60eb50f97f37f5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 23 Jul 2024 14:08:57 +0300 Subject: [PATCH 306/566] Add pretty-deterministic output for cram test --- src/config/options.schema.json | 4 ++-- src/framework/analysisResult.ml | 13 +++++++++++ .../00-sanity/33-hoare-over-paths.t | 22 +++++++++---------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 0cb1b6ee67..4243c29590 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -79,9 +79,9 @@ "result": { "title": "result", "description": - "Result style: none, fast_xml, json, pretty, json-messages, sarif.", + "Result style: none, fast_xml, json, pretty, pretty-deterministic, json-messages, sarif.", "type": "string", - "enum": ["none", "fast_xml", "json", "pretty", "json-messages", "sarif"], + "enum": ["none", "fast_xml", "json", "pretty", "pretty-deterministic", "json-messages", "sarif"], "default": "none" }, "solver": { diff --git a/src/framework/analysisResult.ml b/src/framework/analysisResult.ml index 41c2bbd2c7..c1d7fde8a5 100644 --- a/src/framework/analysisResult.ml +++ b/src/framework/analysisResult.ml @@ -45,6 +45,18 @@ struct let defline () = dprintf "OTHERS -> Not available\n" in dprintf "@[Mapping {\n @[%t%t@]}@]" content defline + let pretty_deterministic () mapping = + let bindings = + to_list mapping + |> List.sort [%ord: ResultNode.t * Range.t] + in + let f dok (key, st) = + dok ++ dprintf "%a ->@? @[%a@]\n" ResultNode.pretty key Range.pretty st + in + let content () = List.fold_left f nil bindings in + let defline () = dprintf "OTHERS -> Not available\n" in + dprintf "@[Mapping {\n @[%t%t@]}@]" content defline + include C let printXml f xs = @@ -91,6 +103,7 @@ struct let out = Messages.get_out result_name !Messages.out in match get_string "result" with | "pretty" -> ignore (fprintf out "%a\n" pretty (Lazy.force table)) + | "pretty-deterministic" -> ignore (fprintf out "%a\n" pretty_deterministic (Lazy.force table)) | "fast_xml" -> let module SH = BatHashtbl.Make (Basetype.RawStrings) in let file2funs = SH.create 100 in diff --git a/tests/regression/00-sanity/33-hoare-over-paths.t b/tests/regression/00-sanity/33-hoare-over-paths.t index 4d34cd4561..5148f5e1f2 100644 --- a/tests/regression/00-sanity/33-hoare-over-paths.t +++ b/tests/regression/00-sanity/33-hoare-over-paths.t @@ -1,4 +1,4 @@ - $ goblint --set ana.path_sens[+] mutex --set result pretty --set outfile pretty.txt 33-hoare-over-paths.c + $ goblint --set ana.path_sens[+] mutex --set result pretty-deterministic --set outfile pretty.txt 33-hoare-over-paths.c [Success][Assert] Assertion "1" will succeed (33-hoare-over-paths.c:11:5-11:24) [Success][Assert] Assertion "1" will succeed (33-hoare-over-paths.c:16:5-16:24) [Info][Deadcode] Logical lines of code (LLoC) summary: @@ -169,9 +169,6 @@ Global { m -> mutex } - Temp { - RETURN -> 0 - } }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, @@ -179,12 +176,13 @@ escape:{}, mutexEvents:(), access:(), - mutex:(lockset:{m}, multiplicity:{}), + mutex:(lockset:{}, multiplicity:{}), race:(), mhp:(), assert:(), - pthreadMutexType:()], map:{}), - (MCP.D:[expRelation:(), + pthreadMutexType:()], map:{})} + 33-hoare-over-paths.c:7:1-34:1(main) -> + PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), mallocWrapper:(wrapper call:Unknown node, unique calls:{}), base:({ Global { @@ -200,18 +198,20 @@ escape:{}, mutexEvents:(), access:(), - mutex:(lockset:{}, multiplicity:{}), + mutex:(lockset:{m}, multiplicity:{}), race:(), mhp:(), assert:(), - pthreadMutexType:()], map:{})} - 33-hoare-over-paths.c:7:1-34:1(main) -> - PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + pthreadMutexType:()], map:{}), + (MCP.D:[expRelation:(), mallocWrapper:(wrapper call:Unknown node, unique calls:{}), base:({ Global { m -> mutex } + Temp { + RETURN -> 0 + } }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, From 62f1e2e0de72df4b09ffcd7d34a86c1616c740af Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 26 Jun 2024 17:43:07 +0300 Subject: [PATCH 307/566] Try to print selenium logs for Gobview test --- scripts/test-gobview.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/test-gobview.py b/scripts/test-gobview.py index f5961108d7..10808a9b62 100644 --- a/scripts/test-gobview.py +++ b/scripts/test-gobview.py @@ -6,6 +6,7 @@ from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from threading import Thread import subprocess @@ -17,6 +18,11 @@ # cleanup def cleanup(browser, thread): print("cleanup") + + # print messages + for entry in browser.get_log('browser'): + print(entry) + browser.close() p.kill() thread.join() @@ -35,6 +41,8 @@ def serve(): print("starting installation of browser\n") options = Options() options.add_argument('headless') +# options.set_capability("loggingPrefs", { 'browser':'ALL' }) +options.set_capability("goog:loggingPrefs", { 'browser':'ALL' }) browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()),options=options) print("finished webdriver installation \n") browser.maximize_window() From 23643df9f573ab88296247fe17a07dc3205db249 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Jul 2024 10:46:11 +0300 Subject: [PATCH 308/566] Add js_of_ocaml upper bound for Gobview --- gobview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gobview b/gobview index 4069f32f82..03b0682f97 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 4069f32f82efdefb43c970fb77ca29671a4b6972 +Subproject commit 03b0682f973eab0d26cf8aea74c63a9e869c9716 From 72713a62ad3a3e09f57ec6b8a70e16cfa09ff76a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Jul 2024 13:31:06 +0300 Subject: [PATCH 309/566] Add initial CHANGELOG for v2.4.0 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d285480259..73b235883f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## v2.4.0 (unreleased) +* Remove unmaintained analyses: spec, file (#1281). +* Add linear two-variable equalities analysis (#1297, #1412, #1466). +* Add callstring, loopfree callstring and context gas analyses (#1038, #1340, #1379, #1427, #1439). +* Add non-relational thread-modular value analyses with thread IDs (#1366, #1398, #1399). +* Add NULL byte array domain (#1076). +* Fix spurious overflow warnings from internal evaluations (#1406, #1411, #1511). +* Refactor non-definite mutex handling to fix unsoundness (#1430, #1500, #1409). +* Fix non-relational thread-modular value analysis unsoundness with ambiguous points-to sets (#1457, #1458). +* Fix mutex type analysis unsoundness and enable it by default (#1414, #1416, #1510). +* Add points-to set refinement on mutex path splitting (#1287, #1343, #1374, #1396, #1407). +* Improve narrowing operators (#1502, #1540, #1543). +* Extract automatic configuration tuning for soundness (#1369). +* Fix many locations in witnesses (#1355, #1372, #1400, #1403). +* Improve output readability (#1294, #1312, #1405, #1497). +* Refactor logging (#1117). +* Modernize all library function specifications (#1029, #688, #1174, #1289, #1447, #1487). +* Remove OCaml 4.10, 4.11, 4.12 and 4.13 support (#1448). + ## v2.3.0 Functionally equivalent to Goblint in SV-COMP 2024. From a00ca1b507b638cffa7404ee5e5bba9ddaa1a586 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Jul 2024 17:15:07 +0300 Subject: [PATCH 310/566] Remove goblint.build-info.js and goblint.sites.js --- gobview | 2 +- src/build-info/build_info_js/dune | 5 ----- src/build-info/build_info_js/dune_build_info.ml | 1 - src/sites/sites_js/dune | 6 ------ src/sites/sites_js/goblint_sites.ml | 6 ------ 5 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 src/build-info/build_info_js/dune delete mode 100644 src/build-info/build_info_js/dune_build_info.ml delete mode 100644 src/sites/sites_js/dune delete mode 100644 src/sites/sites_js/goblint_sites.ml diff --git a/gobview b/gobview index 03b0682f97..1895e62dab 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 03b0682f973eab0d26cf8aea74c63a9e869c9716 +Subproject commit 1895e62dab67cfb05a5981bcfd7f36d46acd2b7e diff --git a/src/build-info/build_info_js/dune b/src/build-info/build_info_js/dune deleted file mode 100644 index 9400f564ff..0000000000 --- a/src/build-info/build_info_js/dune +++ /dev/null @@ -1,5 +0,0 @@ -; goblint.build-info implementation which works with js_of_ocaml and doesn't use dune-build-info -(library - (name goblint_build_info_js) - (public_name goblint.build-info.js) - (implements goblint.build-info)) diff --git a/src/build-info/build_info_js/dune_build_info.ml b/src/build-info/build_info_js/dune_build_info.ml deleted file mode 100644 index 002015cd31..0000000000 --- a/src/build-info/build_info_js/dune_build_info.ml +++ /dev/null @@ -1 +0,0 @@ -let statically_linked_libraries = [] diff --git a/src/sites/sites_js/dune b/src/sites/sites_js/dune deleted file mode 100644 index 4e20871974..0000000000 --- a/src/sites/sites_js/dune +++ /dev/null @@ -1,6 +0,0 @@ -; goblint.sites implementation which works with js_of_ocaml and doesn't use dune-site -(library - (name goblint_sites_js) - (public_name goblint.sites.js) - (implements goblint.sites) - (modules goblint_sites)) diff --git a/src/sites/sites_js/goblint_sites.ml b/src/sites/sites_js/goblint_sites.ml deleted file mode 100644 index 3a7b353064..0000000000 --- a/src/sites/sites_js/goblint_sites.ml +++ /dev/null @@ -1,6 +0,0 @@ -let lib = [] -let lib_stub_include = [] -let lib_stub_src = [] -let lib_runtime_include = [] -let lib_runtime_src = [] -let conf = [] From 5a50fa1e16e63ab3ebe5004dce2a3bf3cb4aba6f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Jul 2024 17:26:08 +0300 Subject: [PATCH 311/566] Unvirtualize goblint.build-info and goblint.sites --- gobview | 2 +- src/build-info/build_info_dune/dune | 6 ------ src/build-info/dune | 8 +------- .../{build_info_dune => }/dune_build_info.ml | 0 src/dune | 8 ++++---- src/sites/dune | 12 +++++------- src/sites/{sites_dune => }/goblint_sites.ml | 0 src/sites/sites_dune/dune | 12 ------------ tests/regression/cfg/util/dune | 4 ++-- tests/unit/dune | 2 +- tests/util/dune | 4 ++-- 11 files changed, 16 insertions(+), 42 deletions(-) delete mode 100644 src/build-info/build_info_dune/dune rename src/build-info/{build_info_dune => }/dune_build_info.ml (100%) rename src/sites/{sites_dune => }/goblint_sites.ml (100%) delete mode 100644 src/sites/sites_dune/dune diff --git a/gobview b/gobview index 1895e62dab..f9ce8bcad3 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 1895e62dab67cfb05a5981bcfd7f36d46acd2b7e +Subproject commit f9ce8bcad3552ad95488bc4988dcc0d5ed57b365 diff --git a/src/build-info/build_info_dune/dune b/src/build-info/build_info_dune/dune deleted file mode 100644 index ec46f4b3d1..0000000000 --- a/src/build-info/build_info_dune/dune +++ /dev/null @@ -1,6 +0,0 @@ -; goblint.build-info implementation which properly uses dune-build-info -(library - (name goblint_build_info_dune) - (public_name goblint.build-info.dune) - (implements goblint.build-info) - (libraries dune-build-info)) diff --git a/src/build-info/dune b/src/build-info/dune index e1a45ef8fc..4ffa1f4550 100644 --- a/src/build-info/dune +++ b/src/build-info/dune @@ -1,15 +1,9 @@ (include_subdirs no) -; virtual library to allow js build (for gobview) without dune-build-info -; dune-build-info seems to be incompatible with js_of_ocaml -; File "gobview/src/.App.eobjs/build_info_data.ml-gen", line 1: -; Error: Could not find the .cmi file for interface -; gobview/src/.App.eobjs/build_info_data.ml-gen. (library (name goblint_build_info) (public_name goblint.build-info) - (libraries batteries.unthreaded) - (virtual_modules dune_build_info)) + (libraries dune-build-info batteries.unthreaded)) (rule (target configVersion.ml) diff --git a/src/build-info/build_info_dune/dune_build_info.ml b/src/build-info/dune_build_info.ml similarity index 100% rename from src/build-info/build_info_dune/dune_build_info.ml rename to src/build-info/dune_build_info.ml diff --git a/src/dune b/src/dune index 5265821b5a..c549fd5d7d 100644 --- a/src/dune +++ b/src/dune @@ -88,7 +88,7 @@ (public_names goblint) (modes byte native) ; https://dune.readthedocs.io/en/stable/dune-files.html#linking-modes (modules goblint) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) + (libraries goblint.lib goblint.sites goblint.build-info goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) @@ -96,7 +96,7 @@ (executable (name privPrecCompare) (modules privPrecCompare) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) + (libraries goblint.lib goblint.sites goblint.build-info goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) @@ -104,7 +104,7 @@ (executable (name apronPrecCompare) (modules apronPrecCompare) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) + (libraries goblint.lib goblint.sites goblint.build-info goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) @@ -112,7 +112,7 @@ (executable (name messagesCompare) (modules messagesCompare) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) + (libraries goblint.lib goblint.sites goblint.build-info goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) diff --git a/src/sites/dune b/src/sites/dune index d8663e37fe..6de3a5a32a 100644 --- a/src/sites/dune +++ b/src/sites/dune @@ -1,12 +1,10 @@ (include_subdirs no) -; virtual library to allow js build (for gobview) without dune-site -; dune-site seems to be incompatible with js_of_ocaml -; File "gobview/src/.App.eobjs/dune_site_data.ml-gen", line 1: -; Error: Could not find the .cmi file for interface -; gobview/src/.App.eobjs/dune_site_data.ml-gen. (library (name goblint_sites) (public_name goblint.sites) - (virtual_modules goblint_sites) - (libraries fpath)) + (libraries dune-site fpath)) + +(generate_sites_module + (module dunesite) + (sites goblint)) diff --git a/src/sites/sites_dune/goblint_sites.ml b/src/sites/goblint_sites.ml similarity index 100% rename from src/sites/sites_dune/goblint_sites.ml rename to src/sites/goblint_sites.ml diff --git a/src/sites/sites_dune/dune b/src/sites/sites_dune/dune deleted file mode 100644 index b7f90a8892..0000000000 --- a/src/sites/sites_dune/dune +++ /dev/null @@ -1,12 +0,0 @@ -; goblint.sites implementation which properly uses dune-site -(library - (name goblint_sites_dune) - (public_name goblint.sites.dune) - (implements goblint.sites) - (modules goblint_sites dunesite) - (private_modules dunesite) ; must also be in modules - (libraries dune-site)) - -(generate_sites_module - (module dunesite) - (sites goblint)) diff --git a/tests/regression/cfg/util/dune b/tests/regression/cfg/util/dune index fb3c5e6899..4c41de07e4 100644 --- a/tests/regression/cfg/util/dune +++ b/tests/regression/cfg/util/dune @@ -6,6 +6,6 @@ goblint_common goblint_lib ; TODO: avoid: extract LoopUnrolling and WitnessUtil node predicates from goblint_lib fpath - goblint.sites.dune - goblint.build-info.dune) + goblint.sites + goblint.build-info) (preprocess (pps ppx_deriving.std))) diff --git a/tests/unit/dune b/tests/unit/dune index 5f0b909a77..6c3083dc1a 100644 --- a/tests/unit/dune +++ b/tests/unit/dune @@ -2,7 +2,7 @@ (test (name mainTest) - (libraries ounit2 qcheck-ounit goblint.std goblint.common goblint.lib goblint.constraint goblint.solver goblint.cdomain.value goblint.sites.dune goblint.build-info.dune) + (libraries ounit2 qcheck-ounit goblint.std goblint.common goblint.lib goblint.constraint goblint.solver goblint.cdomain.value goblint.sites goblint.build-info) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall)) diff --git a/tests/util/dune b/tests/util/dune index 6637217651..fb630bd15b 100644 --- a/tests/util/dune +++ b/tests/util/dune @@ -5,7 +5,7 @@ goblint_std goblint_lib yaml - goblint.sites.dune - goblint.build-info.dune) + goblint.sites + goblint.build-info) (flags :standard -open Goblint_std) (preprocess (pps ppx_deriving.std))) From a5ab08b1a744f647ea4ef86a24173b6d545c103f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Jul 2024 17:27:39 +0300 Subject: [PATCH 312/566] Remove unused goblint.build-info and goblint.sites dependency from most executables --- gobview | 2 +- src/dune | 8 ++++---- src/index.mld | 6 ------ tests/regression/cfg/util/dune | 4 +--- tests/unit/dune | 2 +- tests/util/dune | 4 +--- 6 files changed, 8 insertions(+), 18 deletions(-) diff --git a/gobview b/gobview index f9ce8bcad3..4e965cf1bb 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit f9ce8bcad3552ad95488bc4988dcc0d5ed57b365 +Subproject commit 4e965cf1bb7be5e7ceef2a40586ea445682cca64 diff --git a/src/dune b/src/dune index c549fd5d7d..2ba497c629 100644 --- a/src/dune +++ b/src/dune @@ -88,7 +88,7 @@ (public_names goblint) (modes byte native) ; https://dune.readthedocs.io/en/stable/dune-files.html#linking-modes (modules goblint) - (libraries goblint.lib goblint.sites goblint.build-info goblint_std) + (libraries goblint.lib goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) @@ -96,7 +96,7 @@ (executable (name privPrecCompare) (modules privPrecCompare) - (libraries goblint.lib goblint.sites goblint.build-info goblint_std) + (libraries goblint.lib goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) @@ -104,7 +104,7 @@ (executable (name apronPrecCompare) (modules apronPrecCompare) - (libraries goblint.lib goblint.sites goblint.build-info goblint_std) + (libraries goblint.lib goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) @@ -112,7 +112,7 @@ (executable (name messagesCompare) (modules messagesCompare) - (libraries goblint.lib goblint.sites goblint.build-info goblint_std) + (libraries goblint.lib goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) diff --git a/src/index.mld b/src/index.mld index f0d63a0fc7..a2ef15482e 100644 --- a/src/index.mld +++ b/src/index.mld @@ -44,15 +44,9 @@ The following libraries provide [goblint] package metadata for executables. {2 Library goblint.build-info} {!modules:Goblint_build_info} -This library is virtual and has the following implementations -- goblint.build-info.dune for native executables, -- goblint.build-info.js for js_of_ocaml executables. {2 Library goblint.sites} {!modules:Goblint_sites} -This library is virtual and has the following implementations -- goblint.sites.dune for native executables, -- goblint.sites.js for js_of_ocaml executables. {1 Independent utilities} diff --git a/tests/regression/cfg/util/dune b/tests/regression/cfg/util/dune index 4c41de07e4..8ab300b531 100644 --- a/tests/regression/cfg/util/dune +++ b/tests/regression/cfg/util/dune @@ -5,7 +5,5 @@ goblint_logs goblint_common goblint_lib ; TODO: avoid: extract LoopUnrolling and WitnessUtil node predicates from goblint_lib - fpath - goblint.sites - goblint.build-info) + fpath) (preprocess (pps ppx_deriving.std))) diff --git a/tests/unit/dune b/tests/unit/dune index 6c3083dc1a..07c87e7822 100644 --- a/tests/unit/dune +++ b/tests/unit/dune @@ -2,7 +2,7 @@ (test (name mainTest) - (libraries ounit2 qcheck-ounit goblint.std goblint.common goblint.lib goblint.constraint goblint.solver goblint.cdomain.value goblint.sites goblint.build-info) + (libraries ounit2 qcheck-ounit goblint.std goblint.common goblint.lib goblint.constraint goblint.solver goblint.cdomain.value) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall)) diff --git a/tests/util/dune b/tests/util/dune index fb630bd15b..d37c38dc7c 100644 --- a/tests/util/dune +++ b/tests/util/dune @@ -4,8 +4,6 @@ batteries.unthreaded goblint_std goblint_lib - yaml - goblint.sites - goblint.build-info) + yaml) (flags :standard -open Goblint_std) (preprocess (pps ppx_deriving.std))) From 40b34346ff77e078a29dbaef2d195fe552402e86 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 30 Jul 2024 11:04:28 +0300 Subject: [PATCH 313/566] Add TODOs about IntDomain refinement --- src/cdomain/value/cdomains/intDomain.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 2d40e6a161..c983e543fa 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -2794,7 +2794,7 @@ module Enums : S with type int_t = Z.t = struct | Inc e, Some (c, m) -> Inc (BISet.filter (contains c m) e) | _ -> a - let refine_with_interval ik a b = a + let refine_with_interval ik a b = a (* TODO: refine inclusion (exclusion?) set *) let refine_with_excl_list ik a b = match b with @@ -3577,7 +3577,7 @@ module IntDomTupleImpl = struct in [(fun (a, b, c, d, e) -> refine_with_excl_list ik (a, b, c, d, e) (to_excl_list (a, b, c, d, e))); (fun (a, b, c, d, e) -> refine_with_incl_list ik (a, b, c, d, e) (to_incl_list (a, b, c, d, e))); - (fun (a, b, c, d, e) -> maybe refine_with_interval ik (a, b, c, d, e) b); + (fun (a, b, c, d, e) -> maybe refine_with_interval ik (a, b, c, d, e) b); (* TODO: get interval across all domains with minimal and maximal *) (fun (a, b, c, d, e) -> maybe refine_with_congruence ik (a, b, c, d, e) d)] let refine ik ((a, b, c, d, e) : t ) : t = @@ -3791,6 +3791,7 @@ module IntDomTupleImpl = struct | _ -> BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) let invariant_ikind e ik ((_, _, _, x_cong, x_intset) as x) = + (* TODO: do refinement before to ensure incl_list being more precise than intervals, etc (https://github.com/goblint/analyzer/pull/1517#discussion_r1693998515), requires refine functions to actually refine that *) let simplify_int fallback = match to_int x with | Some v -> From e5f097c4eea6214c71aee679fcf6c6374cdb1152 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 26 Jan 2024 12:53:35 +0200 Subject: [PATCH 314/566] Enable ana.sv-comp.functions in some Apron tests using __VERIFIER_nondet_int --- tests/regression/36-apron/45-context.c | 2 +- tests/regression/36-apron/46-no-context.c | 2 +- tests/regression/36-apron/47-no-context-attribute.c | 2 +- tests/regression/36-apron/48-context-attribute.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/36-apron/45-context.c b/tests/regression/36-apron/45-context.c index 94328af97f..eda945abd5 100644 --- a/tests/regression/36-apron/45-context.c +++ b/tests/regression/36-apron/45-context.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --enable ana.relation.context +// SKIP PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --enable ana.relation.context extern int __VERIFIER_nondet_int(); #include diff --git a/tests/regression/36-apron/46-no-context.c b/tests/regression/36-apron/46-no-context.c index bf115cee24..640784b913 100644 --- a/tests/regression/36-apron/46-no-context.c +++ b/tests/regression/36-apron/46-no-context.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --disable ana.relation.context +// SKIP PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --disable ana.relation.context extern int __VERIFIER_nondet_int(); #include diff --git a/tests/regression/36-apron/47-no-context-attribute.c b/tests/regression/36-apron/47-no-context-attribute.c index 90b58cdc28..cf111f5ffc 100644 --- a/tests/regression/36-apron/47-no-context-attribute.c +++ b/tests/regression/36-apron/47-no-context-attribute.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --enable ana.relation.context +// SKIP PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --enable ana.relation.context extern int __VERIFIER_nondet_int(); #include diff --git a/tests/regression/36-apron/48-context-attribute.c b/tests/regression/36-apron/48-context-attribute.c index 5e5ecf01fe..3304c20388 100644 --- a/tests/regression/36-apron/48-context-attribute.c +++ b/tests/regression/36-apron/48-context-attribute.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --disable ana.relation.context +// SKIP PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --disable ana.relation.context extern int __VERIFIER_nondet_int(); #include From bc85d30c340457c8599360981c90a0768693b7dc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 26 Jan 2024 18:37:24 +0200 Subject: [PATCH 315/566] Make SetDomain hash non-commutative --- src/domain/setDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/setDomain.ml b/src/domain/setDomain.ml index 9b545a78ee..c552363f3d 100644 --- a/src/domain/setDomain.ml +++ b/src/domain/setDomain.ml @@ -184,7 +184,7 @@ struct end ) - let hash x = fold (fun x y -> y + Base.hash x) x 0 + let hash x = fold (fun x y -> 13 * y + Base.hash x) x 0 let relift x = map Base.relift x From 2a714b81804eb1bb4c06ba36aee34aa18bc9de10 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 30 Jul 2024 15:53:17 +0300 Subject: [PATCH 316/566] Simplify and generalize malloc fix in relational mutex-meet --- src/analyses/apron/relationPriv.apron.ml | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 78a06dc227..ad4d26dfbf 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -479,25 +479,19 @@ struct let get_mutex_inits' = keep_only_protected_globals ask m get_mutex_inits in RD.join get_m get_mutex_inits' - let get_mutex_global_g_with_mutex_inits (ask:Q.ask) getg g = + let get_mutex_global_g_with_mutex_inits ask getg g = let g_var = AV.global g in let get_mutex_global_g = - let r = - if Param.handle_atomic then - (* Unprotected invariant is one big relation. *) - RD.keep_vars (getg (V.mutex atomic_mutex)) [g_var] - else - getg (V.global g) - in - if RD.is_bot r && (ask.f (Queries.IsAllocVar g)) then - (* malloc'ed blobs may not have a value here yet *) - RD.top () + if Param.handle_atomic then ( + (* Unprotected invariant is one big relation. *) + RD.keep_vars (getg (V.mutex atomic_mutex)) [g_var] + ) else - r + getg (V.global g) in let get_mutex_inits = getg V.mutex_inits in let get_mutex_inits' = RD.keep_vars get_mutex_inits [g_var] in - if not (RD.mem_var get_mutex_inits' g_var) then (* TODO: is this just a workaround for an escape bug? https://github.com/goblint/analyzer/pull/1354/files#r1498882657 *) + if RD.mem_var get_mutex_global_g g_var && not (RD.mem_var get_mutex_inits' g_var) then (* TODO: is this just a workaround for an escape bug? https://github.com/goblint/analyzer/pull/1354/files#r1498882657 *) (* This is an escaped variable whose value was never side-effected to get_mutex_inits' *) get_mutex_global_g else From c18061e000b830157f17f09c544320a83f9d756a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 31 Jul 2024 11:26:54 +0300 Subject: [PATCH 317/566] Remove pthreadMutexType from ghost witness tests It is now enabled by default and default mutex type is assumed non-recursive now. --- .../regression/13-privatized/04-priv_multi.t | 4 +-- tests/regression/13-privatized/25-struct_nr.t | 2 +- tests/regression/13-privatized/74-mutex.c | 4 +-- tests/regression/13-privatized/74-mutex.t | 26 +++++++++---------- tests/regression/13-privatized/92-idx_priv.t | 2 +- tests/regression/29-svcomp/16-atomic_priv.t | 4 +-- .../regression/36-apron/12-traces-min-rpb1.t | 2 +- .../56-witness/64-ghost-multiple-protecting.c | 2 +- .../56-witness/64-ghost-multiple-protecting.t | 6 ++--- .../56-witness/65-ghost-ambiguous-lock.c | 2 +- .../56-witness/65-ghost-ambiguous-lock.t | 2 +- .../56-witness/66-ghost-alloc-lock.c | 8 +++--- .../56-witness/66-ghost-alloc-lock.t | 26 +++++++++---------- .../56-witness/67-ghost-no-unlock.c | 2 +- .../56-witness/67-ghost-no-unlock.t | 2 +- .../56-witness/68-ghost-ambiguous-idx.t | 2 +- 16 files changed, 48 insertions(+), 48 deletions(-) diff --git a/tests/regression/13-privatized/04-priv_multi.t b/tests/regression/13-privatized/04-priv_multi.t index 576c89ad4d..b1a45dd917 100644 --- a/tests/regression/13-privatized/04-priv_multi.t +++ b/tests/regression/13-privatized/04-priv_multi.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 04-priv_multi.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 04-priv_multi.c [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) [Warning][Deadcode] Function 'dispose' has dead code: @@ -174,7 +174,7 @@ Flow-insensitive invariants as location invariants. - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --enable witness.invariant.flow_insensitive-as-location 04-priv_multi.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --enable witness.invariant.flow_insensitive-as-location 04-priv_multi.c [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) [Warning][Deadcode] Function 'dispose' has dead code: diff --git a/tests/regression/13-privatized/25-struct_nr.t b/tests/regression/13-privatized/25-struct_nr.t index 342cfaf99c..88f205a431 100644 --- a/tests/regression/13-privatized/25-struct_nr.t +++ b/tests/regression/13-privatized/25-struct_nr.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 25-struct_nr.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 25-struct_nr.c [Success][Assert] Assertion "glob1 == 5" will succeed (25-struct_nr.c:26:3-26:30) [Success][Assert] Assertion "t == 5" will succeed (25-struct_nr.c:16:3-16:26) [Success][Assert] Assertion "glob1 == -10" will succeed (25-struct_nr.c:18:3-18:32) diff --git a/tests/regression/13-privatized/74-mutex.c b/tests/regression/13-privatized/74-mutex.c index 7c57688238..8ed9448b7b 100644 --- a/tests/regression/13-privatized/74-mutex.c +++ b/tests/regression/13-privatized/74-mutex.c @@ -29,8 +29,8 @@ void* producer() int main() { pthread_t tid; - pthread_mutexattr_t mutexattr; pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_NORMAL); - pthread_mutex_init(&m, &mutexattr); + + pthread_mutex_init(&m, 0); pthread_create(&tid, 0, producer, 0); pthread_mutex_lock(&m); diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index f6f2fa8463..8999d394ec 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -1,11 +1,11 @@ - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) [Warning][Deadcode] Logical lines of code (LLoC) summary: - live: 15 + live: 14 dead: 1 - total lines: 16 + total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: total generation entries: 9 @@ -84,14 +84,14 @@ Flow-insensitive invariants as location invariants. - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --enable witness.invariant.flow_insensitive-as-location 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --enable witness.invariant.flow_insensitive-as-location 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) [Warning][Deadcode] Logical lines of code (LLoC) summary: - live: 15 + live: 14 dead: 1 - total lines: 16 + total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: total generation entries: 9 @@ -138,9 +138,9 @@ Earlyglobs shouldn't cause protected writes in multithreaded mode from being imm [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) [Warning][Deadcode] Logical lines of code (LLoC) summary: - live: 15 + live: 14 dead: 1 - total lines: 16 + total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Race] Memory locations race summary: safe: 1 @@ -150,14 +150,14 @@ Earlyglobs shouldn't cause protected writes in multithreaded mode from being imm Same with mutex-meet. - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) [Warning][Deadcode] Logical lines of code (LLoC) summary: - live: 15 + live: 14 dead: 1 - total lines: 16 + total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: total generation entries: 9 @@ -241,9 +241,9 @@ Should also work with earlyglobs. [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) [Warning][Deadcode] Logical lines of code (LLoC) summary: - live: 15 + live: 14 dead: 1 - total lines: 16 + total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Race] Memory locations race summary: safe: 1 diff --git a/tests/regression/13-privatized/92-idx_priv.t b/tests/regression/13-privatized/92-idx_priv.t index bd6db7e2ef..b157dfed4b 100644 --- a/tests/regression/13-privatized/92-idx_priv.t +++ b/tests/regression/13-privatized/92-idx_priv.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 92-idx_priv.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 92-idx_priv.c [Success][Assert] Assertion "data == 0" will succeed (92-idx_priv.c:22:3-22:29) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 diff --git a/tests/regression/29-svcomp/16-atomic_priv.t b/tests/regression/29-svcomp/16-atomic_priv.t index 83bc201a6c..eea47295d5 100644 --- a/tests/regression/29-svcomp/16-atomic_priv.t +++ b/tests/regression/29-svcomp/16-atomic_priv.t @@ -1,4 +1,4 @@ - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection-atomic --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 16-atomic_priv.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection-atomic --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 16-atomic_priv.c [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:12:3-12:33) [Success][Assert] Assertion "myglobal == 6" will succeed (16-atomic_priv.c:14:3-14:33) [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:16:3-16:33) @@ -43,7 +43,7 @@ Non-atomic privatization: - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 16-atomic_priv.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 16-atomic_priv.c [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:12:3-12:33) [Success][Assert] Assertion "myglobal == 6" will succeed (16-atomic_priv.c:14:3-14:33) [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:16:3-16:33) diff --git a/tests/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t index 1c3253afbc..df34013d16 100644 --- a/tests/regression/36-apron/12-traces-min-rpb1.t +++ b/tests/regression/36-apron/12-traces-min-rpb1.t @@ -1,4 +1,4 @@ - $ goblint --enable warn.deterministic --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 12-traces-min-rpb1.c --enable ana.apron.invariant.diff-box + $ goblint --enable warn.deterministic --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 12-traces-min-rpb1.c --enable ana.apron.invariant.diff-box [Warning][Assert] Assertion "g == h" is unknown. (12-traces-min-rpb1.c:27:3-27:26) [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:16:3-16:26) [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:29:3-29:26) diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.c b/tests/regression/56-witness/64-ghost-multiple-protecting.c index 589aa92bff..699d133f2b 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.c +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType +// PARAM: --set ana.activated[+] mutexGhosts #include #include int g1, g2; diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.t b/tests/regression/56-witness/64-ghost-multiple-protecting.t index 943934a7be..e78d0d75aa 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.t +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 @@ -144,7 +144,7 @@ protection doesn't have precise protected invariant for g2. type: assertion format: C - $ goblint --set ana.base.privatization protection-read --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization protection-read --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 @@ -295,7 +295,7 @@ protection-read has precise protected invariant for g2. type: assertion format: C - $ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 diff --git a/tests/regression/56-witness/65-ghost-ambiguous-lock.c b/tests/regression/56-witness/65-ghost-ambiguous-lock.c index b1df0ee2e8..f45334e755 100644 --- a/tests/regression/56-witness/65-ghost-ambiguous-lock.c +++ b/tests/regression/56-witness/65-ghost-ambiguous-lock.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType +// PARAM: --set ana.activated[+] mutexGhosts #include #include diff --git a/tests/regression/56-witness/65-ghost-ambiguous-lock.t b/tests/regression/56-witness/65-ghost-ambiguous-lock.t index d7d57d8a00..a6e0c12b74 100644 --- a/tests/regression/56-witness/65-ghost-ambiguous-lock.t +++ b/tests/regression/56-witness/65-ghost-ambiguous-lock.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 65-ghost-ambiguous-lock.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 65-ghost-ambiguous-lock.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 23 dead: 0 diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.c b/tests/regression/56-witness/66-ghost-alloc-lock.c index 2c1028564a..073540b9db 100644 --- a/tests/regression/56-witness/66-ghost-alloc-lock.c +++ b/tests/regression/56-witness/66-ghost-alloc-lock.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set ana.malloc.unique_address_count 1 +// PARAM: --set ana.activated[+] mutexGhosts --set ana.malloc.unique_address_count 1 #include #include @@ -18,11 +18,11 @@ void *t_fun(void *arg) { return NULL; } -int main() { pthread_mutexattr_t attr; pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); // https://github.com/goblint/analyzer/pull/1414 +int main() { m1 = malloc(sizeof(pthread_mutex_t)); - pthread_mutex_init(m1, &attr); + pthread_mutex_init(m1, NULL); m2 = malloc(sizeof(pthread_mutex_t)); - pthread_mutex_init(m2, &attr); + pthread_mutex_init(m2, NULL); pthread_t id; pthread_create(&id, NULL, t_fun, NULL); diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.t b/tests/regression/56-witness/66-ghost-alloc-lock.t index e4268ec1cb..8e45272538 100644 --- a/tests/regression/56-witness/66-ghost-alloc-lock.t +++ b/tests/regression/56-witness/66-ghost-alloc-lock.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set ana.malloc.unique_address_count 1 --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 66-ghost-alloc-lock.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.malloc.unique_address_count 1 --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 66-ghost-alloc-lock.c [Success][Assert] Assertion "g1 == 0" will succeed (66-ghost-alloc-lock.c:31:3-31:27) [Success][Assert] Assertion "g2 == 0" will succeed (66-ghost-alloc-lock.c:34:3-34:27) [Info][Deadcode] Logical lines of code (LLoC) summary: @@ -24,7 +24,7 @@ column: 3 function: main - entry_type: ghost_update - variable: alloc_m817990718_locked + variable: alloc_m861095507_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -33,7 +33,7 @@ column: 3 function: main - entry_type: ghost_update - variable: alloc_m817990718_locked + variable: alloc_m861095507_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -42,7 +42,7 @@ column: 3 function: t_fun - entry_type: ghost_update - variable: alloc_m817990718_locked + variable: alloc_m861095507_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c @@ -51,7 +51,7 @@ column: 3 function: main - entry_type: ghost_update - variable: alloc_m817990718_locked + variable: alloc_m861095507_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c @@ -60,7 +60,7 @@ column: 3 function: t_fun - entry_type: ghost_update - variable: alloc_m334174073_locked + variable: alloc_m559918035_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -69,7 +69,7 @@ column: 3 function: main - entry_type: ghost_update - variable: alloc_m334174073_locked + variable: alloc_m559918035_locked expression: "1" location: file_name: 66-ghost-alloc-lock.c @@ -78,7 +78,7 @@ column: 3 function: t_fun - entry_type: ghost_update - variable: alloc_m334174073_locked + variable: alloc_m559918035_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c @@ -87,7 +87,7 @@ column: 3 function: main - entry_type: ghost_update - variable: alloc_m334174073_locked + variable: alloc_m559918035_locked expression: "0" location: file_name: 66-ghost-alloc-lock.c @@ -101,23 +101,23 @@ type: int initial: "0" - entry_type: ghost_variable - variable: alloc_m817990718_locked + variable: alloc_m861095507_locked scope: global type: int initial: "0" - entry_type: ghost_variable - variable: alloc_m334174073_locked + variable: alloc_m559918035_locked scope: global type: int initial: "0" - entry_type: flow_insensitive_invariant flow_insensitive_invariant: - string: '! multithreaded || (alloc_m817990718_locked || g2 == 0)' + string: '! multithreaded || (alloc_m861095507_locked || g2 == 0)' type: assertion format: C - entry_type: flow_insensitive_invariant flow_insensitive_invariant: - string: '! multithreaded || (alloc_m334174073_locked || g1 == 0)' + string: '! multithreaded || (alloc_m559918035_locked || g1 == 0)' type: assertion format: C - entry_type: flow_insensitive_invariant diff --git a/tests/regression/56-witness/67-ghost-no-unlock.c b/tests/regression/56-witness/67-ghost-no-unlock.c index fc10b919d0..69ad571118 100644 --- a/tests/regression/56-witness/67-ghost-no-unlock.c +++ b/tests/regression/56-witness/67-ghost-no-unlock.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType +// PARAM: --set ana.activated[+] mutexGhosts #include #include diff --git a/tests/regression/56-witness/67-ghost-no-unlock.t b/tests/regression/56-witness/67-ghost-no-unlock.t index aed0ac3414..85b7a0b897 100644 --- a/tests/regression/56-witness/67-ghost-no-unlock.t +++ b/tests/regression/56-witness/67-ghost-no-unlock.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 67-ghost-no-unlock.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 67-ghost-no-unlock.c [Success][Assert] Assertion "g1 == 0" will succeed (67-ghost-no-unlock.c:24:3-24:27) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 diff --git a/tests/regression/56-witness/68-ghost-ambiguous-idx.t b/tests/regression/56-witness/68-ghost-ambiguous-idx.t index 9f50ab7429..02cecfd8f6 100644 --- a/tests/regression/56-witness/68-ghost-ambiguous-idx.t +++ b/tests/regression/56-witness/68-ghost-ambiguous-idx.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.activated[+] pthreadMutexType --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 68-ghost-ambiguous-idx.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 68-ghost-ambiguous-idx.c [Warning][Assert] Assertion "data == 0" is unknown. (68-ghost-ambiguous-idx.c:24:3-24:29) [Warning][Unknown] unlocking mutex (m[4]) which may not be held (68-ghost-ambiguous-idx.c:25:3-25:30) [Info][Deadcode] Logical lines of code (LLoC) summary: From 6055e8dd7e768d2b061c16b9da61a579e120b146 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 31 Jul 2024 11:29:15 +0300 Subject: [PATCH 318/566] Activate abortUnless in svcomp-ghost conf also --- conf/svcomp-ghost.json | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/svcomp-ghost.json b/conf/svcomp-ghost.json index 84f127eb91..108b261322 100644 --- a/conf/svcomp-ghost.json +++ b/conf/svcomp-ghost.json @@ -31,6 +31,7 @@ "region", "thread", "threadJoins", + "abortUnless", "mutexGhosts", "pthreadMutexType" ], From 6e793142a53781763f9232d31302c5d21fea3b47 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 31 Jul 2024 11:31:52 +0300 Subject: [PATCH 319/566] Update TODO comment about base earlyglobs flow-insensitive invariants --- src/analyses/base.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4ae2fc711c..782b5662c6 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1247,8 +1247,8 @@ struct let query_invariant_global ctx g = if GobConfig.get_bool "ana.base.invariant.enabled" then ( (* Currently these global invariants are only sound with earlyglobs enabled for both single- and multi-threaded programs. - Otherwise, the values of globals in single-threaded mode are not accounted for. *) - (* TODO: account for single-threaded values without earlyglobs. *) + Otherwise, the values of globals in single-threaded mode are not accounted for. + They are also made sound without earlyglobs using the multithreaded mode ghost variable. *) match g with | `Left g' -> (* priv *) let inv = Priv.invariant_global (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) g' in From 10a63d0acc1248d1399eb2ed397b3253db185002 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 31 Jul 2024 12:41:00 +0300 Subject: [PATCH 320/566] Make unavailable on BSD On FreeBSD gcc package installs cpp13 (not cpp-13), not cpp (which is clang's). Also, tcsh has no compgen to look up existing cpp-s. --- goblint.opam | 2 +- goblint.opam.locked | 2 +- goblint.opam.template | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/goblint.opam b/goblint.opam index 7fe108f682..f342a85069 100644 --- a/goblint.opam +++ b/goblint.opam @@ -93,7 +93,7 @@ build: [ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! -available: os-distribution != "alpine" & arch != "arm64" +available: os-family != "bsd" & os-distribution != "alpine" & arch != "arm64" pin-depends: [ # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] diff --git a/goblint.opam.locked b/goblint.opam.locked index 252459d517..9d1b688e14 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -128,7 +128,7 @@ build: [ ["dune" "install" "-p" name "--create-install-files" name] ] dev-repo: "git+https://github.com/goblint/analyzer.git" -available: os-distribution != "alpine" & arch != "arm64" +available: os-family != "bsd" & os-distribution != "alpine" & arch != "arm64" conflicts: [ "result" {< "1.5"} "ez-conf-lib" {= "1"} diff --git a/goblint.opam.template b/goblint.opam.template index a730d5c064..fc8f1d419b 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,6 +1,6 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! -available: os-distribution != "alpine" & arch != "arm64" +available: os-family != "bsd" & os-distribution != "alpine" & arch != "arm64" pin-depends: [ # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] From bc8811c8034ec1600823923e4cdc625bfe949d1c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 2 Aug 2024 10:06:18 +0300 Subject: [PATCH 321/566] Finalize CHANGELOG for v2.4.0 --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73b235883f..420cc7145e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,11 @@ -## v2.4.0 (unreleased) +## v2.4.0 * Remove unmaintained analyses: spec, file (#1281). * Add linear two-variable equalities analysis (#1297, #1412, #1466). * Add callstring, loopfree callstring and context gas analyses (#1038, #1340, #1379, #1427, #1439). * Add non-relational thread-modular value analyses with thread IDs (#1366, #1398, #1399). * Add NULL byte array domain (#1076). * Fix spurious overflow warnings from internal evaluations (#1406, #1411, #1511). -* Refactor non-definite mutex handling to fix unsoundness (#1430, #1500, #1409). +* Refactor non-definite mutex handling to fix unsoundness (#1430, #1500, #1503, #1409). * Fix non-relational thread-modular value analysis unsoundness with ambiguous points-to sets (#1457, #1458). * Fix mutex type analysis unsoundness and enable it by default (#1414, #1416, #1510). * Add points-to set refinement on mutex path splitting (#1287, #1343, #1374, #1396, #1407). From cffe5c2e35fd1912a5c927bd1a5377035a4bee7e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 2 Aug 2024 10:22:28 +0300 Subject: [PATCH 322/566] Replace goblint-cil pin with published 2.0.4, remove pins for v2.4.0 release --- dune-project | 2 +- goblint.opam | 10 +++++----- goblint.opam.locked | 9 +-------- goblint.opam.template | 8 ++++---- gobview | 2 +- src/common/util/cilType.ml | 1 + src/incremental/compareAST.ml | 1 + 7 files changed, 14 insertions(+), 19 deletions(-) diff --git a/dune-project b/dune-project index 3927e77bdd..f854518419 100644 --- a/dune-project +++ b/dune-project @@ -37,7 +37,7 @@ Goblint includes analyses for assertions, overflows, deadlocks, etc and can be e "concurrency")) (depends (ocaml (>= 4.14)) - (goblint-cil (>= 2.0.3)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. + (goblint-cil (>= 2.0.4)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. (batteries (>= 3.5.1)) (zarith (>= 1.10)) (yojson (>= 2.0.0)) diff --git a/goblint.opam b/goblint.opam index f342a85069..6809044a7e 100644 --- a/goblint.opam +++ b/goblint.opam @@ -37,7 +37,7 @@ bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ "dune" {>= "3.7"} "ocaml" {>= "4.14"} - "goblint-cil" {>= "2.0.3"} + "goblint-cil" {>= "2.0.4"} "batteries" {>= "3.5.1"} "zarith" {>= "1.10"} "yojson" {>= "2.0.0"} @@ -94,10 +94,10 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & arch != "arm64" -pin-depends: [ - # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed - [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] -] +# pin-depends: [ + # published goblint-cil 2.0.4 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] +# ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} ] diff --git a/goblint.opam.locked b/goblint.opam.locked index 9d1b688e14..5c1eb95709 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -64,7 +64,7 @@ depends: [ "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} - "goblint-cil" {= "2.0.3"} + "goblint-cil" {= "2.0.4"} "hex" {= "1.5.0"} "integers" {= "0.7.0"} "json-data-encoding" {= "1.0.1"} @@ -136,13 +136,6 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] -# TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 -pin-depends: [ - [ - "goblint-cil.2.0.3" - "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" - ] -] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} description: """\ Goblint is a sound static analysis framework for C programs using abstract interpretation. diff --git a/goblint.opam.template b/goblint.opam.template index fc8f1d419b..53260fb576 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,10 +1,10 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & arch != "arm64" -pin-depends: [ - # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed - [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] -] +# pin-depends: [ + # published goblint-cil 2.0.4 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] +# ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} ] diff --git a/gobview b/gobview index 03b0682f97..76e42c34d3 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 03b0682f973eab0d26cf8aea74c63a9e869c9716 +Subproject commit 76e42c34d36bd2ab6900efd661a972ba4824f065 diff --git a/src/common/util/cilType.ml b/src/common/util/cilType.ml index a484a228bd..5a473b7d60 100644 --- a/src/common/util/cilType.ml +++ b/src/common/util/cilType.ml @@ -698,6 +698,7 @@ struct | AAddrOf of t | AIndex of t * t | AQuestion of t * t * t + | AAssign of t * t [@@deriving eq, ord, hash] let name () = "attrparam" diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index f3de153658..2449cdac47 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -210,6 +210,7 @@ and eq_attrparam (a: attrparam) (b: attrparam) ~(rename_mapping: rename_mapping) | AAddrOf attrparam1, AAddrOf attrparam2 -> eq_attrparam attrparam1 attrparam2 ~rename_mapping ~acc | AIndex (left1, right1), AIndex (left2, right2) -> eq_attrparam left1 left2 ~rename_mapping ~acc &&>> eq_attrparam right1 right2 ~acc | AQuestion (left1, middle1, right1), AQuestion (left2, middle2, right2) -> eq_attrparam left1 left2 ~rename_mapping ~acc &&>> eq_attrparam middle1 middle2 ~acc &&>> eq_attrparam right1 right2 ~acc + | AAssign (left1, right1), AAssign (left2, right2) -> eq_attrparam left1 left2 ~rename_mapping ~acc &&>> eq_attrparam right1 right2 ~acc | a, b -> a = b, rename_mapping and eq_attribute (a: attribute) (b: attribute) ~(acc: (typ * typ) list) ~(rename_mapping: rename_mapping) = match a, b with From 8dd27a167601b98d79f58747060464bd06122289 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 2 Aug 2024 10:24:11 +0300 Subject: [PATCH 323/566] Fix unused parameter error in BenchSet --- bench/basic/benchSet.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/basic/benchSet.ml b/bench/basic/benchSet.ml index 14eb03be82..ae38c156da 100644 --- a/bench/basic/benchSet.ml +++ b/bench/basic/benchSet.ml @@ -73,7 +73,7 @@ let () = ] ); "const" @> lazy ( - let args = ((fun x -> 42), set1) in + let args = ((fun _ -> 42), set1) in throughputN 1 [ ("map1", map1, args); ("map2", map2, args); From e09d31b3002d1e9f87d85d8a31a52c56a759486f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 2 Aug 2024 10:28:33 +0300 Subject: [PATCH 324/566] Make available on arm64 --- goblint.opam | 2 +- goblint.opam.locked | 2 +- goblint.opam.template | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/goblint.opam b/goblint.opam index 6809044a7e..85068b4114 100644 --- a/goblint.opam +++ b/goblint.opam @@ -93,7 +93,7 @@ build: [ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! -available: os-family != "bsd" & os-distribution != "alpine" & arch != "arm64" +available: os-family != "bsd" & os-distribution != "alpine" # pin-depends: [ # published goblint-cil 2.0.4 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] diff --git a/goblint.opam.locked b/goblint.opam.locked index 5c1eb95709..ca03f318b4 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -128,7 +128,7 @@ build: [ ["dune" "install" "-p" name "--create-install-files" name] ] dev-repo: "git+https://github.com/goblint/analyzer.git" -available: os-family != "bsd" & os-distribution != "alpine" & arch != "arm64" +available: os-family != "bsd" & os-distribution != "alpine" conflicts: [ "result" {< "1.5"} "ez-conf-lib" {= "1"} diff --git a/goblint.opam.template b/goblint.opam.template index 53260fb576..605f07751c 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,6 +1,6 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! -available: os-family != "bsd" & os-distribution != "alpine" & arch != "arm64" +available: os-family != "bsd" & os-distribution != "alpine" # pin-depends: [ # published goblint-cil 2.0.4 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] From 0ad776f1ab4204878813c02b04b709cfdc3928a1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 2 Aug 2024 10:28:42 +0300 Subject: [PATCH 325/566] Add M1 OSX to unlocked CI --- .github/workflows/unlocked.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 6a9eced42c..0c4433d0af 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -33,6 +33,8 @@ jobs: - os: ubuntu-latest ocaml-compiler: 4.14.x z3: true + - os: macos-latest + ocaml-compiler: 4.14.x # customize name to use readable string for apron instead of just a boolean # workaround for missing ternary operator: https://github.com/actions/runner/issues/409 From ad95965788dcd2accb884df584966fd77fa48569 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 2 Aug 2024 14:53:49 +0300 Subject: [PATCH 326/566] Update releasing documentation --- docs/developer-guide/releasing.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index d875c0d3bf..7530d9ad20 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -24,6 +24,8 @@ All changes must be committed because the working tree is not checked. + The warning `[FAIL] opam field doc cannot be parsed by dune-release` is fine and can be ignored (see ). + 8. Check that "unlocked" workflow passes on GitHub Actions. It can be run manually on the release branch for checking. @@ -36,12 +38,13 @@ 1. Pull Docker image: `docker pull ocaml/opam:ubuntu-22.04-ocaml-4.14` (or newer). 2. Extract distribution archive. 3. Run Docker container in extracted directory: `docker run -it --rm -v $(pwd):/goblint ocaml/opam:ubuntu-22.04-ocaml-4.14` (or newer). - 4. Navigate to distribution archive inside Docker container: `cd /goblint`. - 5. Install and test package from distribution archive: `opam-2.1 install --with-test .`. - 6. Activate opam environment: `eval $(opam env)`. - 7. Check version: `goblint --version`. - 8. Check that analysis works: `goblint -v tests/regression/04-mutex/01-simple_rc.c`. - 9. Exit Docker container. + 4. Update opam-repository from git: `opam-2.1 repository add git git+https://github.com/ocaml/opam-repository.git && opam-2.1 update`. + 5. Navigate to distribution archive inside Docker container: `cd /goblint`. + 6. Install and test package from distribution archive: `opam-2.1 install --with-test .`. + 7. Activate opam environment: `eval $(opam env)`. + 8. Check version: `goblint --version`. + 9. Check that analysis works: `goblint -v tests/regression/04-mutex/01-simple_rc.c`. + 10. Exit Docker container. 12. Temporarily enable Zenodo GitHub webhook. From d46438a3c6b8b643db0d3a1edfe8894ac72fd7f0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 2 Aug 2024 14:57:22 +0300 Subject: [PATCH 327/566] Partially revert "Replace goblint-cil pin with published 2.0.4, remove pins for v2.4.0 release" This reverts commit cffe5c2e35fd1912a5c927bd1a5377035a4bee7e. --- goblint.opam | 7 +++---- goblint.opam.locked | 6 ++++++ goblint.opam.template | 7 +++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/goblint.opam b/goblint.opam index 85068b4114..37cd36bf6e 100644 --- a/goblint.opam +++ b/goblint.opam @@ -94,10 +94,9 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" -# pin-depends: [ - # published goblint-cil 2.0.4 is currently up-to-date, so no pin needed - # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] -# ] +pin-depends: [ + [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#317e26d48b06d5cdc4acff3df1a6824587052b53" ] +] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} ] diff --git a/goblint.opam.locked b/goblint.opam.locked index ca03f318b4..7b23cc8a18 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -136,6 +136,12 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +pin-depends: [ + [ + "goblint-cil.2.0.4" + "git+https://github.com/goblint/cil.git#317e26d48b06d5cdc4acff3df1a6824587052b53" + ] +] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} description: """\ Goblint is a sound static analysis framework for C programs using abstract interpretation. diff --git a/goblint.opam.template b/goblint.opam.template index 605f07751c..3adc8e05c0 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,10 +1,9 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" -# pin-depends: [ - # published goblint-cil 2.0.4 is currently up-to-date, so no pin needed - # [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] -# ] +pin-depends: [ + [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#317e26d48b06d5cdc4acff3df1a6824587052b53" ] +] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} ] From 3ad6b396681e0cb936eba09c4d7b6ebed882a0a2 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 4 Aug 2024 14:02:53 +0200 Subject: [PATCH 328/566] Typo --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 29fa74c5a9..4e523fe1ee 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2168,7 +2168,7 @@ struct end | _, _ when get_bool "sem.unknown_function.spawn" -> (* TODO: Remove sem.unknown_function.spawn check because it is (and should be) really done in LibraryFunctions. - But here we consider all non-ThreadCrate functions also unknown, so old-style LibraryFunctions access + But here we consider all non-ThreadCreate functions also unknown, so old-style LibraryFunctions access definitions using `Write would still spawn because they are not truly unknown functions (missing from LibraryFunctions). Need this to not have memmove spawn in SV-COMP. *) let shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = false } args in From 64d392de0ce64e2fee2b43ad42c0788856a7310b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 5 Aug 2024 11:13:43 +0300 Subject: [PATCH 329/566] Unvendor ppx_blob Version 0.8.0 includes the previously vendored fix for `(lang dune 3.0)`. --- dune-project | 1 + goblint.opam | 1 + goblint.opam.locked | 1 + src/vendor/ppx_blob/LICENSE.txt | 24 -------------------- src/vendor/ppx_blob/README.md | 1 - src/vendor/ppx_blob/src/dune | 5 ----- src/vendor/ppx_blob/src/ppx_blob.ml | 35 ----------------------------- 7 files changed, 3 insertions(+), 65 deletions(-) delete mode 100644 src/vendor/ppx_blob/LICENSE.txt delete mode 100644 src/vendor/ppx_blob/README.md delete mode 100644 src/vendor/ppx_blob/src/dune delete mode 100644 src/vendor/ppx_blob/src/ppx_blob.ml diff --git a/dune-project b/dune-project index f854518419..6ea0734887 100644 --- a/dune-project +++ b/dune-project @@ -45,6 +45,7 @@ Goblint includes analyses for assertions, overflows, deadlocks, etc and can be e (ppx_deriving (>= 6.0.2)) (ppx_deriving_hash (>= 0.1.2)) (ppx_deriving_yojson (>= 3.7.0)) + (ppx_blob (>= 0.8.0)) (ounit2 :with-test) (qcheck-ounit :with-test) (odoc :with-doc) diff --git a/goblint.opam b/goblint.opam index 37cd36bf6e..6ea4183992 100644 --- a/goblint.opam +++ b/goblint.opam @@ -45,6 +45,7 @@ depends: [ "ppx_deriving" {>= "6.0.2"} "ppx_deriving_hash" {>= "0.1.2"} "ppx_deriving_yojson" {>= "3.7.0"} + "ppx_blob" {>= "0.8.0"} "ounit2" {with-test} "qcheck-ounit" {with-test} "odoc" {with-doc} diff --git a/goblint.opam.locked b/goblint.opam.locked index 7b23cc8a18..ed6424dbd3 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -85,6 +85,7 @@ depends: [ "ordering" {= "3.16.0"} "ounit2" {= "2.2.7" & with-test} "pp" {= "1.2.0"} + "ppx_blob" {= "0.9.0"} "ppx_derivers" {= "1.2.1"} "ppx_deriving" {= "6.0.2"} "ppx_deriving_hash" {= "0.1.2"} diff --git a/src/vendor/ppx_blob/LICENSE.txt b/src/vendor/ppx_blob/LICENSE.txt deleted file mode 100644 index 00d2e135a7..0000000000 --- a/src/vendor/ppx_blob/LICENSE.txt +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to \ No newline at end of file diff --git a/src/vendor/ppx_blob/README.md b/src/vendor/ppx_blob/README.md deleted file mode 100644 index fdd7448a32..0000000000 --- a/src/vendor/ppx_blob/README.md +++ /dev/null @@ -1 +0,0 @@ -ppx_blob fix for `(lang dune 3.0)` vendored from . diff --git a/src/vendor/ppx_blob/src/dune b/src/vendor/ppx_blob/src/dune deleted file mode 100644 index 112b1d07c0..0000000000 --- a/src/vendor/ppx_blob/src/dune +++ /dev/null @@ -1,5 +0,0 @@ -(library - (name ppx_blob) - ; (public_name ppx_blob) - (kind ppx_rewriter) - (libraries ppxlib)) \ No newline at end of file diff --git a/src/vendor/ppx_blob/src/ppx_blob.ml b/src/vendor/ppx_blob/src/ppx_blob.ml deleted file mode 100644 index 622a99f8f6..0000000000 --- a/src/vendor/ppx_blob/src/ppx_blob.ml +++ /dev/null @@ -1,35 +0,0 @@ -open Ppxlib - -let location_errorf ~loc = - Format.ksprintf (fun err -> - raise (Ocaml_common.Location.Error (Ocaml_common.Location.error ~loc err)) - ) - -let find_file_path ~loc file_name = - let dirname = loc.Ocaml_common.Location.loc_start.pos_fname |> Filename.dirname in - let relative_path = Filename.concat dirname file_name in - List.find Sys.file_exists [relative_path; file_name] - -let get_blob ~loc file_name = - try - let file_path = find_file_path ~loc file_name in - let c = open_in_bin file_path in - let s = String.init (in_channel_length c) (fun _ -> input_char c) in - close_in c; - s - with _ -> - location_errorf ~loc "[%%blob] could not find or load file %s" file_name - -let expand ~ctxt file_name = - let loc = Expansion_context.Extension.extension_point_loc ctxt in - Ast_builder.Default.estring ~loc (get_blob ~loc file_name) - -let extension = - Extension.V3.declare "blob" Extension.Context.expression - Ast_pattern.(single_expr_payload (estring __)) - expand - -let rule = Ppxlib.Context_free.Rule.extension extension - -;; -Driver.register_transformation ~rules:[rule] "ppx_blob" \ No newline at end of file From 636e8ff1e1b8e9653f83704a3e0e2785ff5eabdc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 7 Aug 2024 11:11:41 +0300 Subject: [PATCH 330/566] Upstream opam file from opam-repository for 2.4.0 --- goblint.opam | 6 +++++- goblint.opam.locked | 2 +- goblint.opam.template | 6 +++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/goblint.opam b/goblint.opam index 37cd36bf6e..24845583f5 100644 --- a/goblint.opam +++ b/goblint.opam @@ -93,7 +93,7 @@ build: [ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! -available: os-family != "bsd" & os-distribution != "alpine" +available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#317e26d48b06d5cdc4acff3df1a6824587052b53" ] ] @@ -103,3 +103,7 @@ depexts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +x-ci-accept-failures: [ + "macos-homebrew" # newer MacOS headers cannot be parsed (https://github.com/ocaml/opam-repository/pull/26307#issuecomment-2258080206) + "opensuse-tumbleweed" # not GNU diff, so some cram tests fail (https://discuss.ocaml.org/t/opensuse-and-opam-tests/14641/2) +] diff --git a/goblint.opam.locked b/goblint.opam.locked index 7b23cc8a18..c629aa6755 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -128,7 +128,7 @@ build: [ ["dune" "install" "-p" name "--create-install-files" name] ] dev-repo: "git+https://github.com/goblint/analyzer.git" -available: os-family != "bsd" & os-distribution != "alpine" +available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") conflicts: [ "result" {< "1.5"} "ez-conf-lib" {= "1"} diff --git a/goblint.opam.template b/goblint.opam.template index 3adc8e05c0..bb70aa29e7 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,6 +1,6 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! -available: os-family != "bsd" & os-distribution != "alpine" +available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#317e26d48b06d5cdc4acff3df1a6824587052b53" ] ] @@ -10,3 +10,7 @@ depexts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +x-ci-accept-failures: [ + "macos-homebrew" # newer MacOS headers cannot be parsed (https://github.com/ocaml/opam-repository/pull/26307#issuecomment-2258080206) + "opensuse-tumbleweed" # not GNU diff, so some cram tests fail (https://discuss.ocaml.org/t/opensuse-and-opam-tests/14641/2) +] From b9caf634f7fcbe006b60d5dc4c5d7e28ca8f74a2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 7 Aug 2024 11:31:12 +0300 Subject: [PATCH 331/566] Fix Ppx_deriving_printable comment indentation --- src/ppx/printable/ppx_deriving_printable.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ppx/printable/ppx_deriving_printable.ml b/src/ppx/printable/ppx_deriving_printable.ml index b8b80d6730..75fd5044fe 100644 --- a/src/ppx/printable/ppx_deriving_printable.ml +++ b/src/ppx/printable/ppx_deriving_printable.ml @@ -12,5 +12,5 @@ let relift_deriving = ReliftDeriver.register () (* TODO: needs https://github.com/ocaml-ppx/ppxlib/pull/124 to include eq, ord, hash *) (* let _ = Ppxlib.Deriving.add_alias "printable" [ - relift_deriving; - ] *) + relift_deriving; + ] *) From d937d68202ff9c4d43721f5439c64c3c4a1a3bbe Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 7 Aug 2024 11:53:06 +0300 Subject: [PATCH 332/566] Add options ana.base.invariant.local and ana.base.invariant.global --- src/analyses/base.ml | 42 ++++++++++++++++++++++++++-------- src/config/options.schema.json | 12 ++++++++++ 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index aa11584f53..f9267ba1d0 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -1183,6 +1183,9 @@ struct not is_alloc || (is_alloc && not (ctx.ask (Queries.IsHeapVar v))) let query_invariant ctx context = + let keep_local = GobConfig.get_bool "ana.base.invariant.local" in + let keep_global = GobConfig.get_bool "ana.base.invariant.global" in + let cpa = ctx.local.BaseDomain.cpa in let ask = Analyses.ask_of_ctx ctx in @@ -1195,6 +1198,13 @@ struct in let module I = ValueDomain.ValueInvariant (Arg) in + let var_filter v = + if is_global ask v then + keep_global + else + keep_local + in + let var_invariant ?offset v = if not (InvariantCil.var_is_heap v) then I.key_invariant v ?offset (Arg.find v) @@ -1205,14 +1215,23 @@ struct if Lval.Set.is_top context.Invariant.lvals then ( if !earlyglobs || ThreadFlag.has_ever_been_multi ask then ( let cpa_invariant = - CPA.fold (fun k v a -> - if not (is_global ask k) then - Invariant.(a && var_invariant k) - else - a - ) cpa Invariant.none + if keep_local then ( + CPA.fold (fun k v a -> + if not (is_global ask k) then + Invariant.(a && var_invariant k) + else + a + ) cpa Invariant.none + ) + else + Invariant.none + in + let priv_vars = + if keep_global then + Priv.invariant_vars ask (priv_getg ctx.global) ctx.local + else + [] in - let priv_vars = Priv.invariant_vars ask (priv_getg ctx.global) ctx.local in let priv_invariant = List.fold_left (fun acc v -> Invariant.(var_invariant v && acc) @@ -1222,7 +1241,10 @@ struct ) else ( CPA.fold (fun k v a -> - Invariant.(a && var_invariant k) + if var_filter k then + Invariant.(a && var_invariant k) + else + a ) cpa Invariant.none ) ) @@ -1230,7 +1252,7 @@ struct Lval.Set.fold (fun k a -> let i = match k with - | (Var v, offset) when not (InvariantCil.var_is_heap v) -> + | (Var v, offset) when var_filter v && not (InvariantCil.var_is_heap v) -> (try I.key_invariant_lval v ~offset ~lval:k (Arg.find v) with Not_found -> Invariant.none) | _ -> Invariant.none in @@ -1245,7 +1267,7 @@ struct Invariant.none let query_invariant_global ctx g = - if GobConfig.get_bool "ana.base.invariant.enabled" then ( + if GobConfig.get_bool "ana.base.invariant.enabled" && GobConfig.get_bool "ana.base.invariant.global" then ( (* Currently these global invariants are only sound with earlyglobs enabled for both single- and multi-threaded programs. Otherwise, the values of globals in single-threaded mode are not accounted for. They are also made sound without earlyglobs using the multithreaded mode ghost variable. *) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 16c9d7e8ef..7121713c33 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -795,6 +795,18 @@ "type": "boolean", "default": true }, + "local": { + "title": "ana.base.invariant.local", + "description": "Keep local variables in invariants", + "type": "boolean", + "default": true + }, + "global": { + "title": "ana.base.invariant.global", + "description": "Keep global variables in invariants", + "type": "boolean", + "default": true + }, "blobs": { "title": "ana.base.invariant.blobs", "description": "Whether to dump assertions about all blobs. Enabling this option may lead to unsound asserts.", From fbc9e62c8c4ca209759fa2be86ba393c64b51cf4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 7 Aug 2024 11:58:02 +0300 Subject: [PATCH 333/566] Add option ana.var_eq.invariant.enabled --- src/analyses/varEq.ml | 2 +- src/config/options.schema.json | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 8ece99d6e8..db1228a3dd 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -564,7 +564,7 @@ struct let r = eq_set_clos e ctx.local in if M.tracing then M.tracel "var_eq" "equalset %a = %a" d_plainexp e Queries.ES.pretty r; r - | Queries.Invariant context when GobConfig.get_bool "witness.invariant.exact" -> (* only exact equalities here *) + | Queries.Invariant context when GobConfig.get_bool "ana.var_eq.invariant.enabled" && 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/config/options.schema.json b/src/config/options.schema.json index 7121713c33..d9174b9aa1 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -1154,6 +1154,26 @@ } }, "additionalProperties": false + }, + "var_eq": { + "title": "ana.var_eq", + "type": "object", + "properties": { + "invariant": { + "title": "ana.var_eq.invariant", + "type": "object", + "properties": { + "enabled": { + "title": "ana.var_eq.invariant.enabled", + "description": "Generate var_eq analysis invariants", + "type": "boolean", + "default": true + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } }, "additionalProperties": false From 58aaf53abd7f0d73cd525648256670da1caf2c9e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 7 Aug 2024 12:10:11 +0300 Subject: [PATCH 334/566] Update svcomp-ghost conf --- conf/svcomp-ghost.json | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/conf/svcomp-ghost.json b/conf/svcomp-ghost.json index 108b261322..dc611695dc 100644 --- a/conf/svcomp-ghost.json +++ b/conf/svcomp-ghost.json @@ -71,6 +71,27 @@ "base": { "arrays": { "domain": "partitioned" + }, + "invariant": { + "local": false, + "global": true + } + }, + "relation": { + "invariant": { + "local": false, + "global": true, + "one-var": false + } + }, + "apron": { + "invariant": { + "diff-box": true + } + }, + "var_eq": { + "invariant": { + "enabled": false } }, "race": { @@ -123,10 +144,10 @@ ] }, "invariant": { - "loop-head": true, + "loop-head": false, "after-lock": true, - "other": true, - "accessed": true, + "other": false, + "accessed": false, "exact": true, "all-locals": false, "flow_insensitive-as-location": true, From f20ed620a1db2acd88b002b096294d8836ed4c55 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 8 Aug 2024 10:58:13 +0300 Subject: [PATCH 335/566] Re-enable witness.invariant.{loop-head,other} in svcomp-ghost conf for flow-insensitive location invariants to work --- conf/svcomp-ghost.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/svcomp-ghost.json b/conf/svcomp-ghost.json index dc611695dc..ce308c5e52 100644 --- a/conf/svcomp-ghost.json +++ b/conf/svcomp-ghost.json @@ -144,9 +144,9 @@ ] }, "invariant": { - "loop-head": false, + "loop-head": true, "after-lock": true, - "other": false, + "other": true, "accessed": false, "exact": true, "all-locals": false, From e630b744c71f370a746ef1b9955f11efa654523a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 12 Aug 2024 14:20:48 +0300 Subject: [PATCH 336/566] Add backtrace markers around LibraryFunctions special calls (issue #1541) --- src/autoTune.ml | 3 +++ src/cdomain/value/util/wideningThresholds.ml | 1 + src/common/dune | 1 + src/common/util/cilfacade.ml | 8 ++++++++ src/transform/evalAssert.ml | 1 + src/util/loopUnrolling.ml | 1 + src/witness/witnessUtil.ml | 1 + 7 files changed, 16 insertions(+) diff --git a/src/autoTune.ml b/src/autoTune.ml index 8ec77739e7..43067b8bbc 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -68,6 +68,7 @@ let functionArgs fd = (ResettableLazy.force functionCallMaps).argLists |> Functi let findMallocWrappers () = let isMalloc f = + Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo f) ~finally:Fun.id @@ fun () -> if LibraryFunctions.is_special f then ( let desc = LibraryFunctions.find f in match functionArgs f with @@ -153,6 +154,7 @@ let disableIntervalContextsInRecursiveFunctions () = let hasFunction pred = let relevant_static var = + Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo var) ~finally:Fun.id @@ fun () -> if LibraryFunctions.is_special var then let desc = LibraryFunctions.find var in GobOption.exists (fun args -> pred (desc.special args)) (functionArgs var) @@ -160,6 +162,7 @@ let hasFunction pred = false in let relevant_dynamic var = + Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo var) ~finally:Fun.id @@ fun () -> if LibraryFunctions.is_special var then let desc = LibraryFunctions.find var in (* We don't really have arguments at hand, so we cheat and just feed it a list of MyCFG.unknown_exp of appropriate length *) diff --git a/src/cdomain/value/util/wideningThresholds.ml b/src/cdomain/value/util/wideningThresholds.ml index 0d93be76ff..42dff238ef 100644 --- a/src/cdomain/value/util/wideningThresholds.ml +++ b/src/cdomain/value/util/wideningThresholds.ml @@ -121,6 +121,7 @@ class extractInvariantsVisitor (exps) = object method! vinst (i: instr) = match i with | Call (_, Lval (Var f, NoOffset), args, _, _) when LibraryFunctions.is_special f -> + Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo f) ~finally:Fun.id @@ fun () -> let desc = LibraryFunctions.find f in begin match desc.special args with | Assert { exp; _ } -> diff --git a/src/common/dune b/src/common/dune index 2d6816a00f..e5aeddde7f 100644 --- a/src/common/dune +++ b/src/common/dune @@ -11,6 +11,7 @@ goblint_logs goblint_config goblint_tracing + goblint_backtrace goblint-cil fpath yojson diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 99430ee8b6..8cd8c2c53f 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -6,6 +6,14 @@ module E = Errormsg include Cilfacade0 +type Goblint_backtrace.mark += FunVarinfo of varinfo + +let () = Goblint_backtrace.register_mark_printer (function + | FunVarinfo var -> + Some ("function " ^ CilType.Varinfo.show var) + | _ -> None (* for other marks *) + ) + (** Command for assigning an id to a varinfo. All varinfos directly created by Goblint should be modified by this method *) let create_var (var: varinfo) = (* Hack: using negative integers should preempt conflicts with ids generated by CIL *) diff --git a/src/transform/evalAssert.ml b/src/transform/evalAssert.ml index 0fee26355b..90da794a93 100644 --- a/src/transform/evalAssert.ml +++ b/src/transform/evalAssert.ml @@ -44,6 +44,7 @@ struct let is_lock exp args = match exp with | Lval(Var v,_) when LibraryFunctions.is_special v -> + Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo v) ~finally:Fun.id @@ fun () -> let desc = LibraryFunctions.find v in (match desc.special args with | Lock _ -> true diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index e433c34b4a..42518708e9 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -311,6 +311,7 @@ class loopUnrollingCallVisitor = object method! vinst = function | Call (_,Lval ((Var info), NoOffset),args,_,_) when LibraryFunctions.is_special info -> ( + Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo info) ~finally:Fun.id @@ fun () -> let desc = LibraryFunctions.find info in match desc.special args with | Malloc _ diff --git a/src/witness/witnessUtil.ml b/src/witness/witnessUtil.ml index 5d22fd1e83..c585b21abc 100644 --- a/src/witness/witnessUtil.ml +++ b/src/witness/witnessUtil.ml @@ -63,6 +63,7 @@ struct List.exists (fun (_, edge) -> match edge with | Proc (_, Lval (Var fv, NoOffset), args) when LibraryFunctions.is_special fv -> + Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo fv) ~finally:Fun.id @@ fun () -> let desc = LibraryFunctions.find fv in begin match desc.special args with | Lock _ -> true From 476f20be1ad83c0857cd975741683d8e66e2608c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 12 Aug 2024 14:23:59 +0300 Subject: [PATCH 337/566] Fix __printf_chk library function specification (closes #1541) --- src/util/library/libraryFunctions.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index e7ff2a4d04..25a90da2d3 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -712,7 +712,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__errno", unknown []); ("__errno_location", unknown []); ("__h_errno_location", unknown []); - ("__printf_chk", unknown [drop "flag" []; drop "format" [r]]); + ("__printf_chk", unknown (drop "flag" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("__fprintf_chk", unknown (drop "stream" [r_deep; w_deep] :: drop "flag" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("__vfprintf_chk", unknown [drop "stream" [r_deep; w_deep]; drop "flag" []; drop "format" [r]; drop "ap" [r_deep]]); ("sysinfo", unknown [drop "info" [w_deep]]); From 4eb58d6be1462f75b113cf87fe63beddb4cce770 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 12 Aug 2024 14:30:38 +0300 Subject: [PATCH 338/566] Add Goblint_backtrace.wrap_val --- src/autoTune.ml | 6 +++--- src/cdomain/value/util/wideningThresholds.ml | 2 +- src/transform/evalAssert.ml | 2 +- src/util/backtrace/goblint_backtrace.ml | 9 +++++++++ src/util/backtrace/goblint_backtrace.mli | 3 +++ src/util/loopUnrolling.ml | 2 +- src/witness/witnessUtil.ml | 2 +- 7 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 43067b8bbc..a0b57c34e4 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -68,7 +68,7 @@ let functionArgs fd = (ResettableLazy.force functionCallMaps).argLists |> Functi let findMallocWrappers () = let isMalloc f = - Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo f) ~finally:Fun.id @@ fun () -> + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo f) @@ fun () -> if LibraryFunctions.is_special f then ( let desc = LibraryFunctions.find f in match functionArgs f with @@ -154,7 +154,7 @@ let disableIntervalContextsInRecursiveFunctions () = let hasFunction pred = let relevant_static var = - Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo var) ~finally:Fun.id @@ fun () -> + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo var) @@ fun () -> if LibraryFunctions.is_special var then let desc = LibraryFunctions.find var in GobOption.exists (fun args -> pred (desc.special args)) (functionArgs var) @@ -162,7 +162,7 @@ let hasFunction pred = false in let relevant_dynamic var = - Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo var) ~finally:Fun.id @@ fun () -> + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo var) @@ fun () -> if LibraryFunctions.is_special var then let desc = LibraryFunctions.find var in (* We don't really have arguments at hand, so we cheat and just feed it a list of MyCFG.unknown_exp of appropriate length *) diff --git a/src/cdomain/value/util/wideningThresholds.ml b/src/cdomain/value/util/wideningThresholds.ml index 42dff238ef..939ed9482f 100644 --- a/src/cdomain/value/util/wideningThresholds.ml +++ b/src/cdomain/value/util/wideningThresholds.ml @@ -121,7 +121,7 @@ class extractInvariantsVisitor (exps) = object method! vinst (i: instr) = match i with | Call (_, Lval (Var f, NoOffset), args, _, _) when LibraryFunctions.is_special f -> - Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo f) ~finally:Fun.id @@ fun () -> + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo f) @@ fun () -> let desc = LibraryFunctions.find f in begin match desc.special args with | Assert { exp; _ } -> diff --git a/src/transform/evalAssert.ml b/src/transform/evalAssert.ml index 90da794a93..8f858d09df 100644 --- a/src/transform/evalAssert.ml +++ b/src/transform/evalAssert.ml @@ -44,7 +44,7 @@ struct let is_lock exp args = match exp with | Lval(Var v,_) when LibraryFunctions.is_special v -> - Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo v) ~finally:Fun.id @@ fun () -> + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo v) @@ fun () -> let desc = LibraryFunctions.find v in (match desc.special args with | Lock _ -> true diff --git a/src/util/backtrace/goblint_backtrace.ml b/src/util/backtrace/goblint_backtrace.ml index 29198c27ea..513753bddb 100644 --- a/src/util/backtrace/goblint_backtrace.ml +++ b/src/util/backtrace/goblint_backtrace.ml @@ -36,6 +36,15 @@ let protect ~(mark: unit -> mark) ~(finally: unit -> unit) work = add_mark work_exn (mark ()); Printexc.raise_with_backtrace work_exn work_bt +(* Copied & modified from protect. *) +let wrap_val ~(mark:mark) work = + try + work () + with work_exn -> + let work_bt = Printexc.get_raw_backtrace () in + add_mark work_exn mark; + Printexc.raise_with_backtrace work_exn work_bt + let mark_printers: (mark -> string option) list ref = ref [] diff --git a/src/util/backtrace/goblint_backtrace.mli b/src/util/backtrace/goblint_backtrace.mli index e2b6ed2913..e53bfd826a 100644 --- a/src/util/backtrace/goblint_backtrace.mli +++ b/src/util/backtrace/goblint_backtrace.mli @@ -24,6 +24,9 @@ val add_mark: exn -> mark -> unit val protect: mark:(unit -> mark) -> finally:(unit -> unit) -> (unit -> 'a) -> 'a (** {!Fun.protect} with additional [~mark] addition to all exceptions. *) +val wrap_val: mark:mark -> (unit -> 'a) -> 'a +(** {!protect} with [~mark] value and without [~finally]. *) + val print_marktrace: out_channel -> exn -> unit (** Print trace of marks of an exception. diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 42518708e9..07c20cb574 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -311,7 +311,7 @@ class loopUnrollingCallVisitor = object method! vinst = function | Call (_,Lval ((Var info), NoOffset),args,_,_) when LibraryFunctions.is_special info -> ( - Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo info) ~finally:Fun.id @@ fun () -> + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo info) @@ fun () -> let desc = LibraryFunctions.find info in match desc.special args with | Malloc _ diff --git a/src/witness/witnessUtil.ml b/src/witness/witnessUtil.ml index c585b21abc..1526b6f95c 100644 --- a/src/witness/witnessUtil.ml +++ b/src/witness/witnessUtil.ml @@ -63,7 +63,7 @@ struct List.exists (fun (_, edge) -> match edge with | Proc (_, Lval (Var fv, NoOffset), args) when LibraryFunctions.is_special fv -> - Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo fv) ~finally:Fun.id @@ fun () -> + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo fv) @@ fun () -> let desc = LibraryFunctions.find fv in begin match desc.special args with | Lock _ -> true From 0d6d3a8e5911476875464b7b6c402908453d8323 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Aug 2024 10:19:51 +0300 Subject: [PATCH 339/566] Pin CIL with attr-enumerator fix for MacOS --- goblint.opam | 2 +- goblint.opam.locked | 2 +- goblint.opam.template | 2 +- src/cdomain/value/cdomains/mutexAttrDomain.ml | 2 +- src/incremental/compareAST.ml | 4 ++-- src/witness/witnessUtil.ml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/goblint.opam b/goblint.opam index b73d2c0329..2ed13448f5 100644 --- a/goblint.opam +++ b/goblint.opam @@ -96,7 +96,7 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#317e26d48b06d5cdc4acff3df1a6824587052b53" ] + [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#04b8a45a7d20425c7b6c8abe1ad094abc063922b" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.locked b/goblint.opam.locked index c629aa6755..a4b9cf6677 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -139,7 +139,7 @@ post-messages: [ pin-depends: [ [ "goblint-cil.2.0.4" - "git+https://github.com/goblint/cil.git#317e26d48b06d5cdc4acff3df1a6824587052b53" + "git+https://github.com/goblint/cil.git#04b8a45a7d20425c7b6c8abe1ad094abc063922b" ] ] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.template b/goblint.opam.template index bb70aa29e7..9f29ceb7a5 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -2,7 +2,7 @@ # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#317e26d48b06d5cdc4acff3df1a6824587052b53" ] + [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#04b8a45a7d20425c7b6c8abe1ad094abc063922b" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/src/cdomain/value/cdomains/mutexAttrDomain.ml b/src/cdomain/value/cdomains/mutexAttrDomain.ml index ea9696d26f..e12818c2c1 100644 --- a/src/cdomain/value/cdomains/mutexAttrDomain.ml +++ b/src/cdomain/value/cdomains/mutexAttrDomain.ml @@ -25,7 +25,7 @@ let recursive_int = lazy ( let res = ref (Z.of_int 2) in (* Use OS X as the default, it doesn't have the enum *) GoblintCil.iterGlobals !Cilfacade.current_file (function | GEnumTag (einfo, _) -> - List.iter (fun (name, exp, _) -> + List.iter (fun (name, _, exp, _) -> if name = "PTHREAD_MUTEX_RECURSIVE" then res := Option.get @@ GoblintCil.getInteger exp ) einfo.eitems diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 2449cdac47..b62cf28f2f 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -178,8 +178,8 @@ and eq_typ_acc ?(fun_parameter_name_comparison_enabled: bool = true) (a: typ) (b if Messages.tracing then Messages.traceu "compareast" "eq_typ_acc %a vs %a" d_type a d_type b; (r, updated_rename_mapping) -and eq_eitems (a: string * exp * location) (b: string * exp * location) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) = match a, b with - (name1, exp1, _l1), (name2, exp2, _l2) -> (name1 = name2, rename_mapping) &&>> eq_exp exp1 exp2 ~acc +and eq_eitems (a: string * attributes * exp * location) (b: string * attributes * exp * location) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) = match a, b with + (name1, attr1, exp1, _l1), (name2, attr2, exp2, _l2) -> (name1 = name2, rename_mapping) &&>> forward_list_equal (eq_attribute ~acc) attr1 attr2 &&>> eq_exp exp1 exp2 ~acc (* Ignore location *) and eq_enuminfo (a: enuminfo) (b: enuminfo) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) = diff --git a/src/witness/witnessUtil.ml b/src/witness/witnessUtil.ml index 5d22fd1e83..ffadaf14e5 100644 --- a/src/witness/witnessUtil.ml +++ b/src/witness/witnessUtil.ml @@ -209,7 +209,7 @@ struct let typ = TEnum (e, []) in let name = "enum " ^ ename in Hashtbl.replace genv name (Cabs2cil.EnvTyp typ, loc); - List.iter (fun (name, exp, loc) -> + List.iter (fun (name, _, exp, loc) -> Hashtbl.replace genv name (Cabs2cil.EnvEnum (exp, typ), loc) ) eitems | Cil.GVar (v, _, loc) From cf9d190c78d9ebac984ca068cc2c5acbb0ce75cd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 16 Aug 2024 15:44:10 +0300 Subject: [PATCH 340/566] Fix base must-writing all invalidated variables set_many writes one after another, so they all end up in protection privatization's P set for example (although it cannot be observed by precision). Nevertheless, this is the morally correct thing to do: an unknown function may or may not write each argument. --- src/analyses/base.ml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 4e523fe1ee..a69b3a2b23 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2045,7 +2045,7 @@ struct List.map mpt exps ) - let invalidate ?(deep=true) ~ctx (st:store) (exps: exp list): store = + let invalidate ~(must: bool) ?(deep=true) ~ctx (st:store) (exps: exp list): store = if M.tracing && exps <> [] then M.tracel "invalidate" "Will invalidate expressions [%a]" (d_list ", " d_plainexp) exps; if exps <> [] then M.info ~category:Imprecise "Invalidating expressions: %a" (d_list ", " d_exp) exps; (* To invalidate a single address, we create a pair with its corresponding @@ -2072,7 +2072,15 @@ struct let vs = List.map (Tuple3.third) invalids' in M.tracel "invalidate" "Setting addresses [%a] to values [%a]" (d_list ", " AD.pretty) addrs (d_list ", " VD.pretty) vs ); - set_many ~ctx st invalids' + (* copied from set_many *) + let f (acc: store) ((lval:AD.t),(typ:Cil.typ),(value:value)): store = + let acc' = set ~ctx acc lval typ value in + if must then + acc' + else + D.join acc acc' + in + List.fold_left f st invalids' let make_entry ?(thread=false) (ctx:(D.t, G.t, C.t, V.t) Analyses.ctx) fundec args: D.t = @@ -2211,8 +2219,8 @@ struct in (* TODO: what about escaped local variables? *) (* invalidate arguments and non-static globals for unknown functions *) - let st' = invalidate ~deep:false ~ctx ctx.local shallow_addrs in - invalidate ~deep:true ~ctx st' deep_addrs + let st' = invalidate ~must:false ~deep:false ~ctx ctx.local shallow_addrs in + invalidate ~must:false ~deep:true ~ctx st' deep_addrs let check_invalid_mem_dealloc ctx special_fn ptr = let has_non_heap_var = AD.exists (function @@ -2302,7 +2310,7 @@ struct let invalidate_ret_lv st = match lv with | Some lv -> if M.tracing then M.tracel "invalidate" "Invalidating lhs %a for function call %s" d_plainlval lv f.vname; - invalidate ~deep:false ~ctx st [Cil.mkAddrOrStartOf lv] + invalidate ~must:true ~deep:false ~ctx st [Cil.mkAddrOrStartOf lv] | None -> st in let addr_type_of_exp exp = @@ -2636,14 +2644,14 @@ struct | Int n when GobOption.exists (Z.equal Z.zero) (ID.to_int n) -> st | Address ret_a -> begin match eval_rv ~ctx st id with - | Thread a when ValueDomain.Threads.is_top a -> invalidate ~ctx st [ret_var] + | Thread a when ValueDomain.Threads.is_top a -> invalidate ~must:true ~ctx st [ret_var] | Thread a -> let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (ctx.global (V.thread x))) (ValueDomain.Threads.elements a)) in (* TODO: is this type right? *) set ~ctx st ret_a (Cilfacade.typeOf ret_var) v - | _ -> invalidate ~ctx st [ret_var] + | _ -> invalidate ~must:true ~ctx st [ret_var] end - | _ -> invalidate ~ctx st [ret_var] + | _ -> invalidate ~must:true ~ctx st [ret_var] in let st' = invalidate_ret_lv st' in Priv.thread_join (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st' From 9c604183f6bd7765fba521dbb7da7efcb9cdf129 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 20 Aug 2024 11:37:34 +0300 Subject: [PATCH 341/566] Add YAML witness ghost_instrumentation entry type --- src/config/options.schema.json | 3 +- src/witness/yamlWitness.ml | 25 +++++++++ src/witness/yamlWitnessType.ml | 99 ++++++++++++++++++++++++++++++++++ tests/util/yamlWitnessStrip.ml | 5 ++ 4 files changed, 131 insertions(+), 1 deletion(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 424992c3de..06b6f26359 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -2648,7 +2648,8 @@ "precondition_loop_invariant_certificate", "invariant_set", "ghost_variable", - "ghost_update" + "ghost_update", + "ghost_instrumentation" ] }, "default": [ diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index d3988f8edb..cc435a38ac 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -160,6 +160,31 @@ struct }; metadata = metadata ~task (); } + + let ghost_variable' ~variable ~type_ ~(initial): GhostInstrumentation.Variable.t = { + name = variable; + scope = "global"; + type_; + initial; + } + + let ghost_update' ~variable ~(expression): GhostInstrumentation.Update.t = { + ghost_variable = variable; + expression; + } + + let ghost_location_update' ~location ~(updates): GhostInstrumentation.LocationUpdate.t = { + location; + updates; + } + + let ghost_instrumentation ~task ~variables ~(location_updates): Entry.t = { + entry_type = GhostInstrumentation { + ghost_variables = variables; + ghost_updates = location_updates; + }; + metadata = metadata ~task (); + } end let yaml_entries_to_file yaml_entries file = diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 4bdb730b82..72afbf432b 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -485,6 +485,99 @@ struct {variable; expression; location} end +module GhostInstrumentation = +struct + + module Variable = + struct + type t = { + name: string; + scope: string; + type_: string; + initial: string; + } + [@@deriving eq, ord, hash] + + let to_yaml {name; scope; type_; initial} = + `O [ + ("name", `String name); + ("scope", `String scope); + ("type", `String type_); + ("initial", `String initial); + ] + + let of_yaml y = + let open GobYaml in + let+ name = y |> find "name" >>= to_string + and+ scope = y |> find "scope" >>= to_string + and+ type_ = y |> find "type" >>= to_string + and+ initial = y |> find "initial" >>= to_string in + {name; scope; type_; initial} + end + + module Update = + struct + type t = { + ghost_variable: string; + expression: string; + } + [@@deriving eq, ord, hash] + + let to_yaml {ghost_variable; expression} = + `O [ + ("ghost_variable", `String ghost_variable); + ("expression", `String expression); + ] + + let of_yaml y = + let open GobYaml in + let+ ghost_variable = y |> find "ghost_variable" >>= to_string + and+ expression = y |> find "expression" >>= to_string in + {ghost_variable; expression} + end + + module LocationUpdate = + struct + type t = { + location: Location.t; + updates: Update.t list; + } + [@@deriving eq, ord, hash] + + let to_yaml {location; updates} = + `O [ + ("location", Location.to_yaml location); + ("updates", `A (List.map Update.to_yaml updates)); + ] + + let of_yaml y = + let open GobYaml in + let+ location = y |> find "location" >>= Location.of_yaml + and+ updates = y |> find "updates" >>= list >>= list_map Update.of_yaml in + {location; updates} + end + + type t = { + ghost_variables: Variable.t list; + ghost_updates: LocationUpdate.t list; + } + [@@deriving eq, ord, hash] + + let entry_type = "ghost_instrumentation" + + let to_yaml' {ghost_variables; ghost_updates} = + [ + ("ghost_variables", `A (List.map Variable.to_yaml ghost_variables)); + ("ghost_updates", `A (List.map LocationUpdate.to_yaml ghost_updates)); + ] + + let of_yaml y = + let open GobYaml in + let+ ghost_variables = y |> find "ghost_variables" >>= list >>= list_map Variable.of_yaml + and+ ghost_updates = y |> find "ghost_updates" >>= list >>= list_map LocationUpdate.of_yaml in + {ghost_variables; ghost_updates} +end + (* TODO: could maybe use GADT, but adds ugly existential layer to entry type pattern matching *) module EntryType = struct @@ -498,6 +591,7 @@ struct | InvariantSet of InvariantSet.t | GhostVariable of GhostVariable.t | GhostUpdate of GhostUpdate.t + | GhostInstrumentation of GhostInstrumentation.t [@@deriving eq, ord, hash] let entry_type = function @@ -510,6 +604,7 @@ struct | InvariantSet _ -> InvariantSet.entry_type | GhostVariable _ -> GhostVariable.entry_type | GhostUpdate _ -> GhostUpdate.entry_type + | GhostInstrumentation _ -> GhostInstrumentation.entry_type let to_yaml' = function | LocationInvariant x -> LocationInvariant.to_yaml' x @@ -521,6 +616,7 @@ struct | InvariantSet x -> InvariantSet.to_yaml' x | GhostVariable x -> GhostVariable.to_yaml' x | GhostUpdate x -> GhostUpdate.to_yaml' x + | GhostInstrumentation x -> GhostInstrumentation.to_yaml' x let of_yaml y = let open GobYaml in @@ -552,6 +648,9 @@ struct else if entry_type = GhostUpdate.entry_type then let+ x = y |> GhostUpdate.of_yaml in GhostUpdate x + else if entry_type = GhostInstrumentation.entry_type then + let+ x = y |> GhostInstrumentation.of_yaml in + GhostInstrumentation x else Error (`Msg ("entry_type " ^ entry_type)) end diff --git a/tests/util/yamlWitnessStrip.ml b/tests/util/yamlWitnessStrip.ml index 211a8a0e1a..4d7b446bab 100644 --- a/tests/util/yamlWitnessStrip.ml +++ b/tests/util/yamlWitnessStrip.ml @@ -26,6 +26,9 @@ struct in {invariant_type} in + let ghost_location_update_strip_file_hash (x: GhostInstrumentation.LocationUpdate.t): GhostInstrumentation.LocationUpdate.t = + {x with location = location_strip_file_hash x.location} + in let entry_type: EntryType.t = match entry_type with | LocationInvariant x -> @@ -46,6 +49,8 @@ struct GhostVariable x (* no location to strip *) | GhostUpdate x -> GhostUpdate {x with location = location_strip_file_hash x.location} + | GhostInstrumentation x -> + GhostInstrumentation {x with ghost_updates = List.map ghost_location_update_strip_file_hash x.ghost_updates} in {entry_type} From d22065396f057689adf10ee950d623fb999bc4b7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 20 Aug 2024 12:27:58 +0300 Subject: [PATCH 342/566] Add ghost_instrumentation support to mutexGhosts --- src/analyses/mutexGhosts.ml | 84 ++++++++++++++++++++-- src/witness/witnessGhost.ml | 22 +++++- tests/regression/13-privatized/74-mutex.t | 87 +++++++++++++++++++++++ 3 files changed, 186 insertions(+), 7 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 7bc4423f04..09afc41baa 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -10,17 +10,19 @@ struct include UnitAnalysis.Spec let name () = "mutexGhosts" - module ThreadCreate = Printable.UnitConf (struct let name = "threadcreate" end) + (* module ThreadCreate = Printable.UnitConf (struct let name = "threadcreate" end) *) module V = struct - include Printable.Either3 (Node) (LockDomain.MustLock) (ThreadCreate) + include Printable.Either3 (Node) (LockDomain.MustLock) (BoolDomain.Bool) let node x = `Left x let lock x = `Middle x - let threadcreate = `Right () + let threadcreate = `Right false + let update = `Right true let is_write_only = function | `Left _ -> false | `Middle _ -> true - | `Right _ -> false + | `Right false -> false + | `Right true -> true end module Locked = @@ -53,9 +55,11 @@ struct | `Bot -> NodeSet.bot () | `Lifted2 (`Lifted2 x) -> x | _ -> failwith "MutexGhosts.threadcreate" + let update = threadcreate let create_node node = `Lifted1 node let create_lock lock = `Lifted2 (`Lifted1 lock) let create_threadcreate threadcreate = `Lifted2 (`Lifted2 threadcreate) + let create_update = create_threadcreate end let mustlock_of_addr (addr: LockDomain.Addr.t): LockDomain.MustLock.t option = @@ -69,6 +73,7 @@ struct | Events.Lock (l, _) when not (LockDomain.Addr.equal l verifier_atomic_addr) -> ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ())); if !AnalysisState.postsolving then ( + ctx.sideg V.update (G.create_update (NodeSet.singleton ctx.prev_node)); let (locked, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in if Locked.cardinal locked > 1 then ( Locked.iter (fun lock -> @@ -81,6 +86,7 @@ struct | Events.Unlock l when not (LockDomain.Addr.equal l verifier_atomic_addr) -> ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ())); if !AnalysisState.postsolving then ( + ctx.sideg V.update (G.create_update (NodeSet.singleton ctx.prev_node)); let (_, unlocked, _) = G.node (ctx.global (V.node ctx.prev_node)) in if Locked.cardinal unlocked > 1 then ( Locked.iter (fun lock -> @@ -91,7 +97,9 @@ struct ); ) | Events.EnterMultiThreaded -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true)) + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true)); + if !AnalysisState.postsolving then + ctx.sideg V.update (G.create_update (NodeSet.singleton ctx.prev_node)); | _ -> () end; ctx.local @@ -113,7 +121,7 @@ struct | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in begin match g with - | `Left g' -> + | `Left g' when YamlWitness.entry_type_enabled YamlWitnessType.GhostVariable.entry_type && YamlWitness.entry_type_enabled YamlWitnessType.GhostUpdate.entry_type -> let (locked, unlocked, multithread) = G.node (ctx.global g) in let g = g' in let entries = @@ -161,6 +169,70 @@ struct entries in entries + | `Right true when YamlWitness.entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type -> + let nodes = G.update (ctx.global g) in + let (variables, location_updates) = NodeSet.fold (fun node (variables, location_updates) -> + let (locked, unlocked, multithread) = G.node (ctx.global (V.node node)) in + let variables' = + (* TODO: do variable_entry-s only once *) + Locked.fold (fun l acc -> + match mustlock_of_addr l with + | Some l when ghost_var_available ctx (Locked l) -> + let variable = WitnessGhost.variable' (Locked l) in + if BatList.mem_cmp YamlWitnessType.GhostInstrumentation.Variable.compare variable acc then (* TODO: be efficient *) + acc + else + variable :: acc + | _ -> + acc + ) (Locked.union locked unlocked) variables + in + let updates = + Locked.fold (fun l acc -> + match mustlock_of_addr l with + | Some l when ghost_var_available ctx (Locked l) -> + let update = WitnessGhost.update' (Locked l) GoblintCil.one in + update :: acc + | _ -> + acc + ) locked [] + in + let updates = + Unlocked.fold (fun l acc -> + match mustlock_of_addr l with + | Some l when ghost_var_available ctx (Locked l) -> + let update = WitnessGhost.update' (Locked l) GoblintCil.zero in + update :: acc + | _ -> + acc + ) unlocked updates + in + let (variables', updates) = + if not (GobConfig.get_bool "exp.earlyglobs") && multithread then ( + if ghost_var_available ctx Multithreaded then ( + let variable = WitnessGhost.variable' Multithreaded in + let update = WitnessGhost.update' Multithreaded GoblintCil.one in + let variables' = + if BatList.mem_cmp YamlWitnessType.GhostInstrumentation.Variable.compare variable variables' then (* TODO: be efficient *) + variables' + else + variable :: variables' + in + (variables', update :: updates) + ) + else + (variables', updates) + ) + else + (variables', updates) + in + let location_update = WitnessGhost.location_update' ~node ~updates in + (variables', location_update :: location_updates) + ) nodes ([], []) + in + let entry = WitnessGhost.instrumentation_entry ~task ~variables ~location_updates in + Queries.YS.singleton entry + | `Left _ -> Queries.Result.top q | `Middle _ -> Queries.Result.top q | `Right _ -> Queries.Result.top q end diff --git a/src/witness/witnessGhost.ml b/src/witness/witnessGhost.ml index 91d513ceae..3535e8a347 100644 --- a/src/witness/witnessGhost.ml +++ b/src/witness/witnessGhost.ml @@ -1,7 +1,7 @@ (** Ghost variables for YAML witnesses. *) let enabled () = - YamlWitness.entry_type_enabled YamlWitnessType.GhostVariable.entry_type && YamlWitness.entry_type_enabled YamlWitnessType.GhostUpdate.entry_type + (YamlWitness.entry_type_enabled YamlWitnessType.GhostVariable.entry_type && YamlWitness.entry_type_enabled YamlWitnessType.GhostUpdate.entry_type) || YamlWitness.entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type module Var = WitnessGhostVar @@ -24,3 +24,23 @@ let update_entry ~task ~node x e = let variable = name_varinfo x in let expression = CilType.Exp.show e in YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression + +let variable' x = + let variable = name_varinfo x in + let type_ = String.trim (CilType.Typ.show (typ x)) in (* CIL printer puts space at the end of some types *) + let initial = CilType.Exp.show (initial x) in + YamlWitness.Entry.ghost_variable' ~variable ~type_ ~initial + +let update' x e = + let variable = name_varinfo x in + let expression = CilType.Exp.show e in + YamlWitness.Entry.ghost_update' ~variable ~expression + +let location_update' ~node ~updates = + let loc = Node.location node in + let location_function = (Node.find_fundec node).svar.vname in + let location = YamlWitness.Entry.location ~location:loc ~location_function in + YamlWitness.Entry.ghost_location_update' ~location ~updates + +let instrumentation_entry ~task ~variables ~location_updates = + YamlWitness.Entry.ghost_instrumentation ~task ~variables ~location_updates diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index 8999d394ec..eec046bcb6 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -148,6 +148,93 @@ Earlyglobs shouldn't cause protected writes in multithreaded mode from being imm unsafe: 0 total memory locations: 1 +Same with ghost_instrumentation entry. + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Witness] witness generation summary: + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: "0" + - name: m_locked + scope: global + type: int + initial: "0" + ghost_updates: + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 38 + column: 3 + function: main + updates: + - ghost_variable: m_locked + expression: "0" + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + updates: + - ghost_variable: m_locked + expression: "1" + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 34 + column: 3 + function: main + updates: + - ghost_variable: multithreaded + expression: "1" + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 23 + column: 5 + function: producer + updates: + - ghost_variable: m_locked + expression: "0" + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 20 + column: 5 + function: producer + updates: + - ghost_variable: m_locked + expression: "1" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m_locked || used == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= used && used <= 1)' + type: assertion + format: C + Same with mutex-meet. $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 74-mutex.c From f79ad180cc9bb3ff2b00b73fcda4368718b8f307 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 20 Aug 2024 12:53:28 +0300 Subject: [PATCH 343/566] Add option for emitting flow_insensitive_invariant-s as invariant_set location_invariant-s in YAML witnesses --- conf/svcomp-ghost.json | 2 +- src/config/options.schema.json | 9 ++-- src/witness/yamlWitness.ml | 52 ++++++++++++++++--- .../regression/13-privatized/04-priv_multi.t | 2 +- tests/regression/13-privatized/74-mutex.t | 40 +++++++++----- 5 files changed, 77 insertions(+), 28 deletions(-) diff --git a/conf/svcomp-ghost.json b/conf/svcomp-ghost.json index ce308c5e52..c4f165960d 100644 --- a/conf/svcomp-ghost.json +++ b/conf/svcomp-ghost.json @@ -150,7 +150,7 @@ "accessed": false, "exact": true, "all-locals": false, - "flow_insensitive-as-location": true, + "flow_insensitive-as": "location_invariant", "exclude-vars": [ "tmp\\(___[0-9]+\\)?", "cond", diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 06b6f26359..11df177cd3 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -2604,11 +2604,12 @@ "type": "boolean", "default": true }, - "flow_insensitive-as-location": { - "title": "witness.invariant.flow_insensitive-as-location", + "flow_insensitive-as": { + "title": "witness.invariant.flow_insensitive-as", "description": "Emit flow-insensitive invariants as location invariants at certain locations.", - "type": "boolean", - "default": false + "type": "string", + "enum": ["flow_insensitive_invariant", "location_invariant", "invariant_set-location_invariant"], + "default": "flow_insensitive_invariant" } }, "additionalProperties": false diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index cc435a38ac..c8217bde19 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -347,22 +347,23 @@ struct entries in + let invariant_global_nodes = lazy (R.ask_global InvariantGlobalNodes) in + (* Generate flow-insensitive invariants *) let entries = if entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type then ( - let ns = lazy (R.ask_global InvariantGlobalNodes) in GHT.fold (fun g v acc -> match g with | `Left g -> (* Spec global *) - begin match R.ask_global (InvariantGlobal (Obj.repr g)), GobConfig.get_bool "witness.invariant.flow_insensitive-as-location" with - | `Lifted inv, false -> + begin match R.ask_global (InvariantGlobal (Obj.repr g)), GobConfig.get_string "witness.invariant.flow_insensitive-as" with + | `Lifted inv, "flow_insensitive_invariant" -> 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 - | `Lifted inv, true -> + | `Lifted inv, "location_invariant" -> (* TODO: or do at location_invariant loop for each node and query if should also do global invariants there? *) let invs = WitnessUtil.InvariantExp.process_exp inv in Queries.NS.fold (fun n acc -> @@ -377,7 +378,8 @@ struct entry :: acc ) acc invs | None -> acc - ) (Lazy.force ns) acc + ) (Lazy.force invariant_global_nodes) acc + | `Lifted _, _ | `Bot, _ | `Top, _ -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) acc end @@ -516,12 +518,12 @@ struct (* Generate invariant set *) let entries = - if entry_type_enabled YamlWitnessType.InvariantSet.entry_type then ( + if entry_type_enabled YamlWitnessType.InvariantSet.entry_type || entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type && GobConfig.get_string "witness.invariant.flow_insensitive-as" = "invariant_set-location_invariant" then ( let invariants = [] in (* Generate location invariants *) let invariants = - if invariant_type_enabled YamlWitnessType.InvariantSet.LocationInvariant.invariant_type then ( + if entry_type_enabled YamlWitnessType.InvariantSet.entry_type && invariant_type_enabled YamlWitnessType.InvariantSet.LocationInvariant.invariant_type then ( LH.fold (fun loc ns acc -> let inv = List.fold_left (fun acc n -> let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in @@ -550,7 +552,7 @@ struct (* Generate loop invariants *) let invariants = - if invariant_type_enabled YamlWitnessType.InvariantSet.LoopInvariant.invariant_type then ( + if entry_type_enabled YamlWitnessType.InvariantSet.entry_type && invariant_type_enabled YamlWitnessType.InvariantSet.LoopInvariant.invariant_type then ( LH.fold (fun loc ns acc -> if WitnessInvariant.emit_loop_head then ( (* TODO: remove double condition? *) let inv = List.fold_left (fun acc n -> @@ -580,6 +582,40 @@ struct invariants in + (* Generate flow-insensitive invariants as location invariants *) + let invariants = + if entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type && GobConfig.get_string "witness.invariant.flow_insensitive-as" = "invariant_set-location_invariant" then ( + GHT.fold (fun g v acc -> + match g with + | `Left g -> (* Spec global *) + begin match R.ask_global (InvariantGlobal (Obj.repr g)) with + | `Lifted inv -> + (* TODO: or do at location_invariant loop for each node and query if should also do global invariants there? *) + let invs = WitnessUtil.InvariantExp.process_exp inv in + Queries.NS.fold (fun n acc -> + let fundec = Node.find_fundec n in + match WitnessInvariant.location_location n with (* if after thread create node happens to be loop node *) + | Some loc -> + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in + List.fold_left (fun acc inv -> + let invariant = CilType.Exp.show inv in + let invariant = Entry.location_invariant' ~location ~invariant in + invariant :: acc + ) acc invs + | None -> acc + ) (Lazy.force invariant_global_nodes) acc + | `Bot | `Top -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) + acc + end + | `Right _ -> (* contexts global *) + acc + ) gh invariants + ) + else + invariants + in + let invariants = List.rev invariants in let entry = Entry.invariant_set ~task ~invariants in entry :: entries diff --git a/tests/regression/13-privatized/04-priv_multi.t b/tests/regression/13-privatized/04-priv_multi.t index b1a45dd917..fd0dad6a39 100644 --- a/tests/regression/13-privatized/04-priv_multi.t +++ b/tests/regression/13-privatized/04-priv_multi.t @@ -174,7 +174,7 @@ Flow-insensitive invariants as location invariants. - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --enable witness.invariant.flow_insensitive-as-location 04-priv_multi.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --set witness.invariant.flow_insensitive-as location_invariant 04-priv_multi.c [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) [Warning][Deadcode] Function 'dispose' has dead code: diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index eec046bcb6..c99cdb6ff9 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -84,7 +84,7 @@ Flow-insensitive invariants as location invariants. - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --enable witness.invariant.flow_insensitive-as-location 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --set witness.invariant.flow_insensitive-as location_invariant 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) @@ -148,9 +148,9 @@ Earlyglobs shouldn't cause protected writes in multithreaded mode from being imm unsafe: 0 total memory locations: 1 -Same with ghost_instrumentation entry. +Same with ghost_instrumentation and invariant_set entries. - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' --set witness.invariant.flow_insensitive-as invariant_set-location_invariant 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) @@ -160,7 +160,7 @@ Same with ghost_instrumentation entry. total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: - total generation entries: 3 + total generation entries: 2 [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 @@ -224,16 +224,28 @@ Same with ghost_instrumentation entry. updates: - ghost_variable: m_locked expression: "1" - - entry_type: flow_insensitive_invariant - flow_insensitive_invariant: - string: '! multithreaded || (m_locked || used == 0)' - type: assertion - format: C - - entry_type: flow_insensitive_invariant - flow_insensitive_invariant: - string: '! multithreaded || (0 <= used && used <= 1)' - type: assertion - format: C + - entry_type: invariant_set + content: + - invariant: + type: location_invariant + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + value: '! multithreaded || (m_locked || used == 0)' + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + value: '! multithreaded || (0 <= used && used <= 1)' + format: c_expression Same with mutex-meet. From e9e652d86cac07bdff43780fbe5467fe46870265 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 20 Aug 2024 12:56:26 +0300 Subject: [PATCH 344/566] Use invariant_set in svcomp-ghost conf --- conf/svcomp-ghost.json | 7 +++---- src/config/options.schema.json | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/conf/svcomp-ghost.json b/conf/svcomp-ghost.json index c4f165960d..cb4d8f1384 100644 --- a/conf/svcomp-ghost.json +++ b/conf/svcomp-ghost.json @@ -136,11 +136,10 @@ }, "yaml": { "enabled": true, - "format-version": "0.1", + "format-version": "2.1", "entry-types": [ "flow_insensitive_invariant", - "ghost_variable", - "ghost_update" + "ghost_instrumentation" ] }, "invariant": { @@ -150,7 +149,7 @@ "accessed": false, "exact": true, "all-locals": false, - "flow_insensitive-as": "location_invariant", + "flow_insensitive-as": "invariant_set-location_invariant", "exclude-vars": [ "tmp\\(___[0-9]+\\)?", "cond", diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 11df177cd3..1101e04ace 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -2630,7 +2630,8 @@ "type": "string", "enum": [ "0.1", - "2.0" + "2.0", + "2.1" ], "default": "0.1" }, From 431b34d18d12ef588c67a170f4a139665e73720c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 20 Aug 2024 15:02:07 +0300 Subject: [PATCH 345/566] Make invariant_set and ghost_instrumentation deterministic in tests --- tests/regression/13-privatized/74-mutex.t | 40 +++++++++++------------ tests/util/yamlWitnessStrip.ml | 12 +++++-- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index c99cdb6ff9..0c2947ab37 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -170,11 +170,11 @@ Same with ghost_instrumentation and invariant_set entries. $ yamlWitnessStrip < witness.yml - entry_type: ghost_instrumentation ghost_variables: - - name: multithreaded + - name: m_locked scope: global type: int initial: "0" - - name: m_locked + - name: multithreaded scope: global type: int initial: "0" @@ -182,21 +182,21 @@ Same with ghost_instrumentation and invariant_set entries. - location: file_name: 74-mutex.c file_hash: $FILE_HASH - line: 38 - column: 3 - function: main + line: 20 + column: 5 + function: producer updates: - ghost_variable: m_locked - expression: "0" + expression: "1" - location: file_name: 74-mutex.c file_hash: $FILE_HASH - line: 36 - column: 3 - function: main + line: 23 + column: 5 + function: producer updates: - ghost_variable: m_locked - expression: "1" + expression: "0" - location: file_name: 74-mutex.c file_hash: $FILE_HASH @@ -209,21 +209,21 @@ Same with ghost_instrumentation and invariant_set entries. - location: file_name: 74-mutex.c file_hash: $FILE_HASH - line: 23 - column: 5 - function: producer + line: 36 + column: 3 + function: main updates: - ghost_variable: m_locked - expression: "0" + expression: "1" - location: file_name: 74-mutex.c file_hash: $FILE_HASH - line: 20 - column: 5 - function: producer + line: 38 + column: 3 + function: main updates: - ghost_variable: m_locked - expression: "1" + expression: "0" - entry_type: invariant_set content: - invariant: @@ -234,7 +234,7 @@ Same with ghost_instrumentation and invariant_set entries. line: 36 column: 3 function: main - value: '! multithreaded || (m_locked || used == 0)' + value: '! multithreaded || (0 <= used && used <= 1)' format: c_expression - invariant: type: location_invariant @@ -244,7 +244,7 @@ Same with ghost_instrumentation and invariant_set entries. line: 36 column: 3 function: main - value: '! multithreaded || (0 <= used && used <= 1)' + value: '! multithreaded || (m_locked || used == 0)' format: c_expression Same with mutex-meet. diff --git a/tests/util/yamlWitnessStrip.ml b/tests/util/yamlWitnessStrip.ml index 4d7b446bab..dff8bfb0cf 100644 --- a/tests/util/yamlWitnessStrip.ml +++ b/tests/util/yamlWitnessStrip.ml @@ -27,7 +27,10 @@ struct {invariant_type} in let ghost_location_update_strip_file_hash (x: GhostInstrumentation.LocationUpdate.t): GhostInstrumentation.LocationUpdate.t = - {x with location = location_strip_file_hash x.location} + { + location = location_strip_file_hash x.location; + updates = List.sort GhostInstrumentation.Update.compare x.updates + } in let entry_type: EntryType.t = match entry_type with @@ -44,13 +47,16 @@ struct | PreconditionLoopInvariantCertificate x -> PreconditionLoopInvariantCertificate {x with target = target_strip_file_hash x.target} | InvariantSet x -> - InvariantSet {content = List.map invariant_strip_file_hash x.content} + InvariantSet {content = List.sort InvariantSet.Invariant.compare (List.map invariant_strip_file_hash x.content)} (* Sort, so order is deterministic regardless of Goblint. *) | GhostVariable x -> GhostVariable x (* no location to strip *) | GhostUpdate x -> GhostUpdate {x with location = location_strip_file_hash x.location} | GhostInstrumentation x -> - GhostInstrumentation {x with ghost_updates = List.map ghost_location_update_strip_file_hash x.ghost_updates} + GhostInstrumentation { (* Sort, so order is deterministic regardless of Goblint. *) + ghost_variables = List.sort GhostInstrumentation.Variable.compare x.ghost_variables; + ghost_updates = List.sort GhostInstrumentation.LocationUpdate.compare (List.map ghost_location_update_strip_file_hash x.ghost_updates); + } in {entry_type} From 3c54188f07c43816d016e95da96255a57e135e39 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 22 Aug 2024 16:15:29 +0300 Subject: [PATCH 346/566] Default location.byte to -1 if missing in JSON --- src/common/util/cilType.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/util/cilType.ml b/src/common/util/cilType.ml index 5a473b7d60..91368052b3 100644 --- a/src/common/util/cilType.ml +++ b/src/common/util/cilType.ml @@ -79,8 +79,8 @@ struct let of_yojson = function | `Assoc l -> - begin match List.assoc_opt "file" l, List.assoc_opt "line" l, List.assoc_opt "column" l, List.assoc_opt "byte" l with - | Some (`String file), Some (`Int line), Some (`Int column), Some (`Int byte) -> + begin match List.assoc_opt "file" l, List.assoc_opt "line" l, List.assoc_opt "column" l, Option.value ~default:(`Int (-1)) (List.assoc_opt "byte" l) with + | Some (`String file), Some (`Int line), Some (`Int column), `Int byte -> let loc = {file; line; column; byte; endLine = -1; endColumn = -1; endByte = -1; synthetic = false} in begin match List.assoc_opt "endLine" l, List.assoc_opt "endColumn" l, List.assoc_opt "endByte" l with | Some (`Int endLine), Some (`Int endColumn), Some (`Int endByte) -> From 2c9955048a13c162fcf74f7ccd67d614c73e3ee5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 28 Aug 2024 17:25:23 +0300 Subject: [PATCH 347/566] Remove ghost_ prefix from ghost_instrumentation update entries --- src/witness/yamlWitness.ml | 2 +- src/witness/yamlWitnessType.ml | 10 +++++----- tests/regression/13-privatized/74-mutex.t | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index c8217bde19..c917361d9b 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -169,7 +169,7 @@ struct } let ghost_update' ~variable ~(expression): GhostInstrumentation.Update.t = { - ghost_variable = variable; + variable; expression; } diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 72afbf432b..cefb0866f3 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -518,22 +518,22 @@ struct module Update = struct type t = { - ghost_variable: string; + variable: string; expression: string; } [@@deriving eq, ord, hash] - let to_yaml {ghost_variable; expression} = + let to_yaml {variable; expression} = `O [ - ("ghost_variable", `String ghost_variable); + ("variable", `String variable); ("expression", `String expression); ] let of_yaml y = let open GobYaml in - let+ ghost_variable = y |> find "ghost_variable" >>= to_string + let+ variable = y |> find "variable" >>= to_string and+ expression = y |> find "expression" >>= to_string in - {ghost_variable; expression} + {variable; expression} end module LocationUpdate = diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index 0c2947ab37..9a11b6846f 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -186,7 +186,7 @@ Same with ghost_instrumentation and invariant_set entries. column: 5 function: producer updates: - - ghost_variable: m_locked + - variable: m_locked expression: "1" - location: file_name: 74-mutex.c @@ -195,7 +195,7 @@ Same with ghost_instrumentation and invariant_set entries. column: 5 function: producer updates: - - ghost_variable: m_locked + - variable: m_locked expression: "0" - location: file_name: 74-mutex.c @@ -204,7 +204,7 @@ Same with ghost_instrumentation and invariant_set entries. column: 3 function: main updates: - - ghost_variable: multithreaded + - variable: multithreaded expression: "1" - location: file_name: 74-mutex.c @@ -213,7 +213,7 @@ Same with ghost_instrumentation and invariant_set entries. column: 3 function: main updates: - - ghost_variable: m_locked + - variable: m_locked expression: "1" - location: file_name: 74-mutex.c @@ -222,7 +222,7 @@ Same with ghost_instrumentation and invariant_set entries. column: 3 function: main updates: - - ghost_variable: m_locked + - variable: m_locked expression: "0" - entry_type: invariant_set content: From 12dadf4f03c8ff9d3e4a1546235722066018fd40 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Sep 2024 11:03:57 +0300 Subject: [PATCH 348/566] Wrap ghost_instrumentation in content --- src/witness/yamlWitnessType.ml | 13 ++- tests/regression/13-privatized/74-mutex.t | 111 +++++++++++----------- 2 files changed, 64 insertions(+), 60 deletions(-) diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index cefb0866f3..b04a2c35bf 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -566,15 +566,18 @@ struct let entry_type = "ghost_instrumentation" let to_yaml' {ghost_variables; ghost_updates} = - [ - ("ghost_variables", `A (List.map Variable.to_yaml ghost_variables)); - ("ghost_updates", `A (List.map LocationUpdate.to_yaml ghost_updates)); + [("content", + `O [ + ("ghost_variables", `A (List.map Variable.to_yaml ghost_variables)); + ("ghost_updates", `A (List.map LocationUpdate.to_yaml ghost_updates)); + ]) ] let of_yaml y = let open GobYaml in - let+ ghost_variables = y |> find "ghost_variables" >>= list >>= list_map Variable.of_yaml - and+ ghost_updates = y |> find "ghost_updates" >>= list >>= list_map LocationUpdate.of_yaml in + let* content = y |> find "content" in + let+ ghost_variables = content |> find "ghost_variables" >>= list >>= list_map Variable.of_yaml + and+ ghost_updates = content |> find "ghost_updates" >>= list >>= list_map LocationUpdate.of_yaml in {ghost_variables; ghost_updates} end diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index 9a11b6846f..478921155e 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -169,61 +169,62 @@ Same with ghost_instrumentation and invariant_set entries. $ yamlWitnessStrip < witness.yml - entry_type: ghost_instrumentation - ghost_variables: - - name: m_locked - scope: global - type: int - initial: "0" - - name: multithreaded - scope: global - type: int - initial: "0" - ghost_updates: - - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 20 - column: 5 - function: producer - updates: - - variable: m_locked - expression: "1" - - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 23 - column: 5 - function: producer - updates: - - variable: m_locked - expression: "0" - - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 34 - column: 3 - function: main - updates: - - variable: multithreaded - expression: "1" - - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 36 - column: 3 - function: main - updates: - - variable: m_locked - expression: "1" - - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 38 - column: 3 - function: main - updates: - - variable: m_locked - expression: "0" + content: + ghost_variables: + - name: m_locked + scope: global + type: int + initial: "0" + - name: multithreaded + scope: global + type: int + initial: "0" + ghost_updates: + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 20 + column: 5 + function: producer + updates: + - variable: m_locked + expression: "1" + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 23 + column: 5 + function: producer + updates: + - variable: m_locked + expression: "0" + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 34 + column: 3 + function: main + updates: + - variable: multithreaded + expression: "1" + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + updates: + - variable: m_locked + expression: "1" + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 38 + column: 3 + function: main + updates: + - variable: m_locked + expression: "0" - entry_type: invariant_set content: - invariant: From 852297b68abbcb05c3f3700098e2bc2f22c93333 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 2 Sep 2024 11:10:31 +0300 Subject: [PATCH 349/566] Add value and format to ghost_instrumentation --- src/witness/yamlWitness.ml | 8 +++-- src/witness/yamlWitnessType.ml | 40 ++++++++++++++++++----- tests/regression/13-privatized/74-mutex.t | 23 +++++++++---- 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index c917361d9b..f8890d8eaa 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -165,12 +165,16 @@ struct name = variable; scope = "global"; type_; - initial; + initial = { + value = initial; + format = "c_expression"; + }; } let ghost_update' ~variable ~(expression): GhostInstrumentation.Update.t = { variable; - expression; + value = expression; + format = "c_expression"; } let ghost_location_update' ~location ~(updates): GhostInstrumentation.LocationUpdate.t = { diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index b04a2c35bf..7834951892 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -488,13 +488,34 @@ end module GhostInstrumentation = struct + module Initial = + struct + type t = { + value: string; + format: string; + } + [@@deriving eq, ord, hash] + + let to_yaml {value; format} = + `O [ + ("value", `String value); + ("format", `String format); + ] + + let of_yaml y = + let open GobYaml in + let+ value = y |> find "value" >>= to_string + and+ format = y |> find "format" >>= to_string in + {value; format} + end + module Variable = struct type t = { name: string; scope: string; type_: string; - initial: string; + initial: Initial.t; } [@@deriving eq, ord, hash] @@ -503,7 +524,7 @@ struct ("name", `String name); ("scope", `String scope); ("type", `String type_); - ("initial", `String initial); + ("initial", Initial.to_yaml initial); ] let of_yaml y = @@ -511,7 +532,7 @@ struct let+ name = y |> find "name" >>= to_string and+ scope = y |> find "scope" >>= to_string and+ type_ = y |> find "type" >>= to_string - and+ initial = y |> find "initial" >>= to_string in + and+ initial = y |> find "initial" >>= Initial.of_yaml in {name; scope; type_; initial} end @@ -519,21 +540,24 @@ struct struct type t = { variable: string; - expression: string; + value: string; + format: string; } [@@deriving eq, ord, hash] - let to_yaml {variable; expression} = + let to_yaml {variable; value; format} = `O [ ("variable", `String variable); - ("expression", `String expression); + ("value", `String value); + ("format", `String format); ] let of_yaml y = let open GobYaml in let+ variable = y |> find "variable" >>= to_string - and+ expression = y |> find "expression" >>= to_string in - {variable; expression} + and+ value = y |> find "value" >>= to_string + and+ format = y |> find "format" >>= to_string in + {variable; value; format} end module LocationUpdate = diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index 478921155e..8a1a7fee5f 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -174,11 +174,15 @@ Same with ghost_instrumentation and invariant_set entries. - name: m_locked scope: global type: int - initial: "0" + initial: + value: "0" + format: c_expression - name: multithreaded scope: global type: int - initial: "0" + initial: + value: "0" + format: c_expression ghost_updates: - location: file_name: 74-mutex.c @@ -188,7 +192,8 @@ Same with ghost_instrumentation and invariant_set entries. function: producer updates: - variable: m_locked - expression: "1" + value: "1" + format: c_expression - location: file_name: 74-mutex.c file_hash: $FILE_HASH @@ -197,7 +202,8 @@ Same with ghost_instrumentation and invariant_set entries. function: producer updates: - variable: m_locked - expression: "0" + value: "0" + format: c_expression - location: file_name: 74-mutex.c file_hash: $FILE_HASH @@ -206,7 +212,8 @@ Same with ghost_instrumentation and invariant_set entries. function: main updates: - variable: multithreaded - expression: "1" + value: "1" + format: c_expression - location: file_name: 74-mutex.c file_hash: $FILE_HASH @@ -215,7 +222,8 @@ Same with ghost_instrumentation and invariant_set entries. function: main updates: - variable: m_locked - expression: "1" + value: "1" + format: c_expression - location: file_name: 74-mutex.c file_hash: $FILE_HASH @@ -224,7 +232,8 @@ Same with ghost_instrumentation and invariant_set entries. function: main updates: - variable: m_locked - expression: "0" + value: "0" + format: c_expression - entry_type: invariant_set content: - invariant: From b148e207ed7df3b65727ec504db3cf1e90442258 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 20 Sep 2024 17:11:37 +0200 Subject: [PATCH 350/566] Modularize out details of the current approach --- src/framework/constraints.ml | 64 ++++++++++++++++++++++++++---------- src/framework/control.ml | 2 +- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 7df4167acd..ef84326be4 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -513,10 +513,41 @@ struct let names x = Format.asprintf "%d" x end +module type Gas = sig + module M:Lattice.S + val startgas: unit -> M.t + val is_exhausted: fundec -> M.t -> bool + val is_any_exhausted: M.t -> bool + val callee_gas: fundec -> M.t -> M.t + val thread_gas: varinfo -> M.t -> M.t +end + +module GlobalGas:Gas = struct + module M = Lattice.Chain (IntConf) + let startgas () = get_int "ana.context.gas_value" + + let is_any_exhausted v = v <= 0 + let is_exhausted _ = is_any_exhausted + + (* callee gas = caller gas - 1 *) + let callee_gas f v = max 0 (v - 1) + let thread_gas f v = max 0 (v - 1) +end + +module PerFunctionGas:Gas = struct + module M = Lattice.Chain (IntConf) + let startgas () = get_int "ana.context.gas_value" + let is_exhausted f v = v <= 0 + let is_any_exhausted v = v <= 0 + let callee_gas f v = max 0 (v - 1) + let thread_gas f v = max 0 (v - 1) +end + + (** Lifts a [Spec] with the context gas variable. The gas variable limits the number of context-sensitively analyzed function calls in a call stack. For every function call the gas is reduced. If the gas is zero, the remaining function calls are analyzed without context-information *) -module ContextGasLifter (S:Spec) - : Spec with module D = Lattice.Prod (S.D) (Lattice.Chain (IntConf)) +module ContextGasLifter (Gas:Gas) (S:Spec) + : Spec with module D = Lattice.Prod (S.D) (Gas.M) and module C = Printable.Option (S.C) (NoContext) and module G = S.G = @@ -529,7 +560,7 @@ struct let printXml f (x,y) = BatPrintf.fprintf f "\n%a\n%a\n" Base1.printXml x Base2.printXml y end - module D = Context_Gas_Prod (S.D) (Lattice.Chain (IntConf)) (* Product of S.D and an integer, tracking the context gas value *) + module D = Context_Gas_Prod (S.D) (Gas.M) (* Product of S.D and an integer, tracking the context gas value *) module C = Printable.Option (S.C) (NoContext) module G = S.G module V = S.V @@ -549,37 +580,36 @@ struct let startcontext () = Some (S.startcontext ()) let name () = S.name ()^" with context gas" - let startstate v = S.startstate v, get_int "ana.context.gas_value" - let exitstate v = S.exitstate v, get_int "ana.context.gas_value" + let startstate v = S.startstate v, Gas.startgas () + let exitstate v = S.exitstate v, Gas.startgas () let morphstate v (d,i) = S.morphstate v d, i let conv (ctx:(D.t,G.t,C.t,V.t) ctx): (S.D.t,G.t,S.C.t,V.t)ctx = - if (cg_val ctx <= 0) - then {ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, cg_val ctx) es) - ; context = (fun () -> ctx_failwith "no context (contextGas = 0)")} - else {ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, cg_val ctx) es) - ; context = (fun () -> Option.get (ctx.context ()))} + {ctx with local = fst ctx.local + ; split = (fun d es -> ctx.split (d, cg_val ctx) es) + ; context = (fun () -> match ctx.context () with Some c -> c | None -> ctx_failwith "no context (contextGas = 0)")} let context ctx fd (d,i) = (* only keep context if the context gas is greater zero *) - if i <= 0 then None else Some (S.context (conv ctx) fd d) + if Gas.is_exhausted fd i then + None + else + Some (S.context (conv ctx) fd d) let enter ctx r f args = - (* callee gas = caller gas - 1 *) - let liftmap_tup = List.map (fun (x,y) -> (x, cg_val ctx), (y, max 0 (cg_val ctx - 1))) in + let liftmap_tup = List.map (fun (x,y) -> (x, cg_val ctx), (y, Gas.callee_gas f (cg_val ctx))) in liftmap_tup (S.enter (conv ctx) r f args) let threadenter ctx ~multiple lval f args = - let liftmap f = List.map (fun (x) -> (x, max 0 (cg_val ctx - 1))) f in + let liftmap d = List.map (fun (x) -> (x, Gas.thread_gas f (cg_val ctx))) d in liftmap (S.threadenter (conv ctx) ~multiple lval f args) let query ctx (type a) (q: a Queries.t):a Queries.result = match q with | Queries.GasExhausted -> + (* The query is only used in a way where overapproximating gas exhaustion is not harmful *) let (d,i) = ctx.local in - (i <= 0) + Gas.is_any_exhausted i | _ -> S.query (conv ctx) q let sync ctx reason = S.sync (conv ctx) reason, cg_val ctx diff --git a/src/framework/control.ml b/src/framework/control.ml index 5e92282210..f28feb6a1e 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -23,7 +23,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( let module S1 = (val (module MCP.MCP2 : Spec) - |> lift (get_int "ana.context.gas_value" >= 0) (module ContextGasLifter) + |> lift (get_int "ana.context.gas_value" >= 0) (module ContextGasLifter(GlobalGas)) |> lift true (module WidenContextLifterSide) (* option checked in functor *) (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) From 05520ec9db47b3cc4706bf9eab3ff90c78667b8a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 20 Sep 2024 17:59:46 +0200 Subject: [PATCH 351/566] Actually rely on lattice structure for gas --- src/framework/constraints.ml | 28 ++++++++++++++-------------- src/framework/control.ml | 7 ++++++- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index ef84326be4..b257d529d8 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -507,10 +507,9 @@ struct end module NoContext = struct let name = "no context" end -module IntConf = -struct - let n () = max_int - let names x = Format.asprintf "%d" x + +module type GasVal = sig + val n: unit -> int end module type Gas = sig @@ -522,9 +521,9 @@ module type Gas = sig val thread_gas: varinfo -> M.t -> M.t end -module GlobalGas:Gas = struct - module M = Lattice.Chain (IntConf) - let startgas () = get_int "ana.context.gas_value" +module GlobalGas(GasVal:GasVal):Gas = struct + module M = Lattice.Chain (struct include GasVal let names x = Format.asprintf "%d" x end) + let startgas () = M.top () (* get_int "ana.context.gas_value" *) let is_any_exhausted v = v <= 0 let is_exhausted _ = is_any_exhausted @@ -534,13 +533,14 @@ module GlobalGas:Gas = struct let thread_gas f v = max 0 (v - 1) end -module PerFunctionGas:Gas = struct - module M = Lattice.Chain (IntConf) - let startgas () = get_int "ana.context.gas_value" - let is_exhausted f v = v <= 0 - let is_any_exhausted v = v <= 0 - let callee_gas f v = max 0 (v - 1) - let thread_gas f v = max 0 (v - 1) +module PerFunctionGas(GasVal:GasVal):Gas = struct + module V = Lattice.Chain (struct include GasVal let names x = Format.asprintf "%d" x end) + module M = MapDomain.MapTop_LiftBot(CilType.Fundec)(V) + let startgas () = M.empty () (* get_int "ana.context.gas_value" *) + let is_exhausted f v = (* v <= 0 *) true + let is_any_exhausted v = (* v <= 0 *) true + let callee_gas f v = v (* max 0 (v - 1) *) + let thread_gas f v = v (* max 0 (v - 1) *) end diff --git a/src/framework/control.ml b/src/framework/control.ml index f28feb6a1e..68c31768c6 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -20,10 +20,15 @@ let spec_module: (module Spec) Lazy.t = lazy ( let open Batteries in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in + let module GasVal = struct + (* Chain lattice has elements [0,n-1], but we want [0,gas_value] *) + let n () = get_int "ana.context.gas_value" + 1 + end + in let module S1 = (val (module MCP.MCP2 : Spec) - |> lift (get_int "ana.context.gas_value" >= 0) (module ContextGasLifter(GlobalGas)) + |> lift (get_int "ana.context.gas_value" >= 0) (module ContextGasLifter(GlobalGas(GasVal))) |> lift true (module WidenContextLifterSide) (* option checked in functor *) (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) From dc84b4a533100bac8c1390e61f03a5e35fe931bb Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 20 Sep 2024 18:09:39 +0200 Subject: [PATCH 352/566] Add PerFunctionGas module --- src/framework/constraints.ml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index b257d529d8..5bc83fbe13 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -530,16 +530,18 @@ module GlobalGas(GasVal:GasVal):Gas = struct (* callee gas = caller gas - 1 *) let callee_gas f v = max 0 (v - 1) - let thread_gas f v = max 0 (v - 1) + let thread_gas f v = max 0 (v - 1) end module PerFunctionGas(GasVal:GasVal):Gas = struct - module V = Lattice.Chain (struct include GasVal let names x = Format.asprintf "%d" x end) - module M = MapDomain.MapTop_LiftBot(CilType.Fundec)(V) - let startgas () = M.empty () (* get_int "ana.context.gas_value" *) - let is_exhausted f v = (* v <= 0 *) true - let is_any_exhausted v = (* v <= 0 *) true - let callee_gas f v = v (* max 0 (v - 1) *) + module G = Lattice.Chain (struct include GasVal let names x = Format.asprintf "%d" x end) + module M = MapDomain.MapTop_LiftBot(CilType.Fundec)(G) + let startgas () = M.empty () + let is_exhausted f v = GobOption.exists (fun g -> g <= 0) (M.find_opt f v) (* v <= 0 *) + let is_any_exhausted v = M.exists (fun _ g -> g <=0) v + let callee_gas f v = + let c = Option.default (G.top ()) (M.find_opt f v) in + M.add f (max 0 c-1) v let thread_gas f v = v (* max 0 (v - 1) *) end From 300a195840ef9d4db014aaef9de6f3c1224fb563 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 20 Sep 2024 18:13:18 +0200 Subject: [PATCH 353/566] Complete `thread_gas` --- src/framework/constraints.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 5bc83fbe13..ce10393d06 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -542,7 +542,12 @@ module PerFunctionGas(GasVal:GasVal):Gas = struct let callee_gas f v = let c = Option.default (G.top ()) (M.find_opt f v) in M.add f (max 0 c-1) v - let thread_gas f v = v (* max 0 (v - 1) *) + let thread_gas f v = + match Cilfacade.find_varinfo_fundec f with + | fd -> + callee_gas fd v + | exception Not_found -> + callee_gas Cil.dummyFunDec v end From 33aee5adde94fe4af03d0f39cb7a819f83d43bad Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 20 Sep 2024 18:25:07 +0200 Subject: [PATCH 354/566] Expose context gas scope via option --- src/config/options.schema.json | 8 ++++++++ src/framework/control.ml | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 7b07c3ca35..03b3d28fa8 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -998,6 +998,14 @@ "type": "integer", "default": -1 }, + "gas_scope": { + "title": "ana.context.gas_scope", + "description": + "Whether the gas should be 'global' (default) or per 'function'", + "type": "string", + "enum": ["global","function"], + "default": "global" + }, "callString_length": { "title": "ana.context.callString_length", "description": "Length of the call string that should be used as context for the call_string and/or call_site analyses. In case the value is zero, the analysis is context-insensitive. For a negative value, an infinite call string is used! For this option to have an effect, one of the analyses in `callstring.ml` must be activated.", diff --git a/src/framework/control.ml b/src/framework/control.ml index 68c31768c6..df57878035 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -28,7 +28,8 @@ let spec_module: (module Spec) Lazy.t = lazy ( let module S1 = (val (module MCP.MCP2 : Spec) - |> lift (get_int "ana.context.gas_value" >= 0) (module ContextGasLifter(GlobalGas(GasVal))) + |> lift (get_int "ana.context.gas_value" >= 0 && get_string "ana.context.gas_scope" = "global") (module ContextGasLifter(GlobalGas(GasVal))) + |> lift (get_int "ana.context.gas_value" >= 0 && get_string "ana.context.gas_scope" = "function") (module ContextGasLifter(PerFunctionGas(GasVal))) |> lift true (module WidenContextLifterSide) (* option checked in functor *) (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) From 3c0ff8c927558a574c7454c6d08d9e8b445d1ebe Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 20 Sep 2024 18:38:07 +0200 Subject: [PATCH 355/566] Two regresion tests --- tests/regression/80-context_gas/23-per-fun.c | 29 ++++++++++++ .../regression/80-context_gas/24-per-fun-ex.c | 44 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/regression/80-context_gas/23-per-fun.c create mode 100644 tests/regression/80-context_gas/24-per-fun-ex.c diff --git a/tests/regression/80-context_gas/23-per-fun.c b/tests/regression/80-context_gas/23-per-fun.c new file mode 100644 index 0000000000..1f64317b0f --- /dev/null +++ b/tests/regression/80-context_gas/23-per-fun.c @@ -0,0 +1,29 @@ +// PARAM: --enable ana.int.interval_set --set ana.context.gas_value 3 --set ana.context.gas_scope function +int nr(int x, int y) { + // Non-recursive, but would fail with global scope as gas for f is exhausted + __goblint_check(x==y); +} + +int f(int x, int y) +{ + int top; + if (x == 0) + { + return y; + } + + if(top) { + nr(5,5); + } else { + nr(6,6); + } + + return f(x - 1, y - 1); +} + +int main() +{ + // main -> f(3,3) -> f(2,2) -> f(1,1) -> f(0,0) -> return 0 + // 4 recursive calls -> boundary (excluded) + __goblint_check(f(3, 3) == 0); // UNKNOWN +} diff --git a/tests/regression/80-context_gas/24-per-fun-ex.c b/tests/regression/80-context_gas/24-per-fun-ex.c new file mode 100644 index 0000000000..ab5d203c45 --- /dev/null +++ b/tests/regression/80-context_gas/24-per-fun-ex.c @@ -0,0 +1,44 @@ +// PARAM: --enable ana.int.interval_set --set ana.context.gas_value 3 --set ana.context.gas_scope function +int nr(int x, int y) { + // Non-recursive, but would fail with global scope as gas for f is exhausted + __goblint_check(x==y); +} + +// Checks that gas is also applied to further functions +int g(int x, int y) +{ + int top; + if (x < -100000) + { + return y; + } + + if(top) { + nr(5,5); + } else { + nr(6,6); + } + + return g(x - 1, y - 1); +} + +int f(int x, int y) +{ + int top; + + if (x == 0) + { + return y; + } + + g(x,y); + + return f(x - 1, y - 1); +} + +int main() +{ + // main -> f(3,3) -> f(2,2) -> f(1,1) -> f(0,0) -> return 0 + // 4 recursive calls -> boundary (excluded) + __goblint_check(f(3, 3) == 0); // UNKNOWN +} From a70c17295e19e8b1568e402ca953fe4bea445087 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 22 Sep 2024 13:46:47 +0200 Subject: [PATCH 356/566] Give JoinCall `f` as an argument to avoid having to overaproxximate gas status --- src/analyses/apron/relationAnalysis.apron.ml | 2 +- src/analyses/apron/relationPriv.apron.ml | 18 +++++++------- src/analyses/base.ml | 2 +- src/analyses/basePriv.ml | 26 ++++++++++---------- src/analyses/basePriv.mli | 2 +- src/analyses/commonPriv.ml | 4 +-- src/analyses/mCP.ml | 4 +-- src/domains/queries.ml | 14 ++++++----- src/framework/analyses.ml | 2 +- src/framework/constraints.ml | 11 +++------ 10 files changed, 42 insertions(+), 43 deletions(-) diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index cb8e8731d9..da14dfff1d 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -772,7 +772,7 @@ struct PCU.RH.replace results ctx.node new_value; end; WideningTokens.with_local_side_tokens (fun () -> - Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread]) + Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread]) ) let init marshal = diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index ff771e692e..15df394d54 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -36,7 +36,7 @@ module type S = val lock: Q.ask -> (V.t -> G.t) -> relation_components_t -> LockDomain.MustLock.t -> relation_components_t val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> LockDomain.MustLock.t -> relation_components_t - val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> relation_components_t + val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread] -> relation_components_t val escape: Node.t -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> EscapeDomain.EscapedVars.t -> relation_components_t val enter_multithreaded: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> relation_components_t @@ -113,10 +113,10 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation () -> + | `JoinCall _ when ConfCheck.branched_thread_creation () -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Normal | `Init | `Thread @@ -385,10 +385,10 @@ struct end | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Normal | `Init | `Thread -> @@ -674,10 +674,10 @@ struct end | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Normal | `Init | `Thread -> @@ -1230,10 +1230,10 @@ struct | `Return -> st (* TODO: implement? *) | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Normal | `Init | `Thread -> diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a69b3a2b23..a1f0b3b08f 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -453,7 +453,7 @@ struct else ctx.local - let sync ctx reason = sync' (reason :> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread]) ctx + let sync ctx reason = sync' (reason :> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread]) ctx let publish_all ctx reason = ignore (sync' reason ctx) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 08413d54b1..946b8f8cc5 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -31,7 +31,7 @@ sig val lock: Q.ask -> (V.t -> G.t) -> BaseComponents (D).t -> LockDomain.MustLock.t -> BaseComponents (D).t val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> LockDomain.MustLock.t -> BaseComponents (D).t - val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> BaseComponents (D).t + val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread] -> BaseComponents (D).t val escape: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> EscapeDomain.EscapedVars.t -> BaseComponents (D).t val enter_multithreaded: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> BaseComponents (D).t @@ -322,10 +322,10 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Return | `Normal | `Init @@ -433,10 +433,10 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Return | `Normal | `Init @@ -802,10 +802,10 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Return | `Normal | `Init @@ -1055,7 +1055,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1111,7 +1111,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1183,7 +1183,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1342,7 +1342,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1521,7 +1521,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1704,7 +1704,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st diff --git a/src/analyses/basePriv.mli b/src/analyses/basePriv.mli index 40e50c2a69..edcf70ec98 100644 --- a/src/analyses/basePriv.mli +++ b/src/analyses/basePriv.mli @@ -20,7 +20,7 @@ sig val lock: Queries.ask -> (V.t -> G.t) -> BaseDomain.BaseComponents (D).t -> LockDomain.MustLock.t -> BaseDomain.BaseComponents (D).t val unlock: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> LockDomain.MustLock.t -> BaseDomain.BaseComponents (D).t - val sync: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> BaseDomain.BaseComponents (D).t + val sync: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread] -> BaseDomain.BaseComponents (D).t val escape: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> EscapeDomain.EscapedVars.t -> BaseDomain.BaseComponents (D).t val enter_multithreaded: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> BaseDomain.BaseComponents (D).t diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 003cdfa96c..915b3da063 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -61,7 +61,7 @@ struct true (** Whether branched thread creation at start nodes of procedures needs to be handled by [sync `JoinCall] of privatization. *) - let branched_thread_creation_at_call (ask:Queries.ask) = + let branched_thread_creation_at_call (ask:Queries.ask) f = let threadflag_active = List.mem "threadflag" (GobConfig.get_string_list "ana.activated") in if threadflag_active then let sens = GobConfig.get_string_list "ana.ctx_sens" in @@ -74,7 +74,7 @@ struct if not threadflag_ctx_sens then true else - ask.f (Queries.GasExhausted) + ask.f (Queries.GasExhausted f) else true end diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index e4c0e261e4..6212b6de90 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -318,10 +318,10 @@ struct f (Result.top ()) (!base_id, spec !base_id, assoc !base_id ctx.local) *) | Queries.DYojson -> `Lifted (D.to_yojson ctx.local) - | Queries.GasExhausted -> + | Queries.GasExhausted f -> if (get_int "ana.context.gas_value" >= 0) then (* There is a lifter above this that will answer it, save to ask *) - ctx.ask (Queries.GasExhausted) + ctx.ask (Queries.GasExhausted f) else (* Abort to avoid infinite recursion *) false diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 5fbb244874..b0ede0cfbf 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -124,9 +124,9 @@ type _ t = | MustTermLoop: stmt -> MustBool.t t | MustTermAllLoops: MustBool.t t | IsEverMultiThreaded: MayBool.t t - | TmpSpecial: Mval.Exp.t -> ML.t t + | TmpSpecial: Mval.Exp.t -> ML.t t | MaySignedOverflow: exp -> MayBool.t t - | GasExhausted: MustBool.t t + | GasExhausted: CilType.Fundec.t -> MustBool.t t type 'a result = 'a @@ -197,7 +197,7 @@ struct | IsEverMultiThreaded -> (module MayBool) | TmpSpecial _ -> (module ML) | MaySignedOverflow _ -> (module MayBool) - | GasExhausted -> (module MustBool) + | GasExhausted _ -> (module MustBool) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -267,7 +267,7 @@ struct | IsEverMultiThreaded -> MayBool.top () | TmpSpecial _ -> ML.top () | MaySignedOverflow _ -> MayBool.top () - | GasExhausted -> MustBool.top () + | GasExhausted _ -> MustBool.top () end (* The type any_query can't be directly defined in Any as t, @@ -334,7 +334,7 @@ struct | Any (TmpSpecial _) -> 56 | Any (IsAllocVar _) -> 57 | Any (MaySignedOverflow _) -> 58 - | Any GasExhausted -> 59 + | Any (GasExhausted _) -> 59 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -389,6 +389,7 @@ struct | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 | Any (MaySignedOverflow e1), Any (MaySignedOverflow e2) -> CilType.Exp.compare e1 e2 + | Any (GasExhausted f1), Any (GasExhausted f2) -> CilType.Fundec.compare f1 f2 (* only argumentless queries should remain *) | _, _ -> Stdlib.compare (order a) (order b) @@ -431,6 +432,7 @@ struct | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv | Any (MaySignedOverflow e) -> CilType.Exp.hash e + | Any (GasExhausted f) -> CilType.Fundec.hash f (* IterSysVars: *) (* - argument is a function and functions cannot be compared in any meaningful way. *) (* - doesn't matter because IterSysVars is always queried from outside of the analysis, so MCP's query caching is not done for it. *) @@ -494,7 +496,7 @@ struct | Any IsEverMultiThreaded -> Pretty.dprintf "IsEverMultiThreaded" | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv | Any (MaySignedOverflow e) -> Pretty.dprintf "MaySignedOverflow %a" CilType.Exp.pretty e - | Any GasExhausted -> Pretty.dprintf "GasExhausted" + | Any (GasExhausted f) -> Pretty.dprintf "GasExhausted %a" CilType.Fundec.pretty f end let to_value_domain_ask (ask: ask) = diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 717507802f..bb494382d7 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -208,7 +208,7 @@ sig val context: (D.t, G.t, C.t, V.t) ctx -> fundec -> D.t -> C.t val startcontext: unit -> C.t - val sync : (D.t, G.t, C.t, V.t) ctx -> [`Normal | `Join | `JoinCall | `Return] -> D.t + val sync : (D.t, G.t, C.t, V.t) ctx -> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return] -> D.t val query : (D.t, G.t, C.t, V.t) ctx -> 'a Queries.t -> 'a Queries.result (** A transfer function which handles the assignment of a rval to a lval, i.e., diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index ce10393d06..b1c3919bd4 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -516,7 +516,6 @@ module type Gas = sig module M:Lattice.S val startgas: unit -> M.t val is_exhausted: fundec -> M.t -> bool - val is_any_exhausted: M.t -> bool val callee_gas: fundec -> M.t -> M.t val thread_gas: varinfo -> M.t -> M.t end @@ -525,7 +524,6 @@ module GlobalGas(GasVal:GasVal):Gas = struct module M = Lattice.Chain (struct include GasVal let names x = Format.asprintf "%d" x end) let startgas () = M.top () (* get_int "ana.context.gas_value" *) - let is_any_exhausted v = v <= 0 let is_exhausted _ = is_any_exhausted (* callee gas = caller gas - 1 *) @@ -538,7 +536,6 @@ module PerFunctionGas(GasVal:GasVal):Gas = struct module M = MapDomain.MapTop_LiftBot(CilType.Fundec)(G) let startgas () = M.empty () let is_exhausted f v = GobOption.exists (fun g -> g <= 0) (M.find_opt f v) (* v <= 0 *) - let is_any_exhausted v = M.exists (fun _ g -> g <=0) v let callee_gas f v = let c = Option.default (G.top ()) (M.find_opt f v) in M.add f (max 0 c-1) v @@ -613,10 +610,10 @@ struct let query ctx (type a) (q: a Queries.t):a Queries.result = match q with - | Queries.GasExhausted -> + | Queries.GasExhausted f -> (* The query is only used in a way where overapproximating gas exhaustion is not harmful *) let (d,i) = ctx.local in - Gas.is_any_exhausted i + Gas.is_exhausted f i | _ -> S.query (conv ctx) q let sync ctx reason = S.sync (conv ctx) reason, cg_val ctx @@ -669,8 +666,8 @@ struct match ctx.prev_node, Cfg.prev ctx.prev_node with | _, _ :: _ :: _ -> (* Join in CFG. *) S.sync ctx `Join - | FunctionEntry _, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) - S.sync ctx `JoinCall + | FunctionEntry f, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) + S.sync ctx (`JoinCall f) | _, _ -> S.sync ctx `Normal let side_context sideg f c = From 1c2c428680c00e27c01abcb34fcb3f462a26aa1c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 22 Sep 2024 13:48:18 +0200 Subject: [PATCH 357/566] Outdated comment --- src/framework/constraints.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index b1c3919bd4..1794443bd1 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -611,7 +611,6 @@ struct let query ctx (type a) (q: a Queries.t):a Queries.result = match q with | Queries.GasExhausted f -> - (* The query is only used in a way where overapproximating gas exhaustion is not harmful *) let (d,i) = ctx.local in Gas.is_exhausted f i | _ -> S.query (conv ctx) q From d2fc47671fcaa2daba7dad032dabeda98fc17171 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 22 Sep 2024 13:50:54 +0200 Subject: [PATCH 358/566] Fix compilation --- src/framework/constraints.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 1794443bd1..b9553b974a 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -524,7 +524,7 @@ module GlobalGas(GasVal:GasVal):Gas = struct module M = Lattice.Chain (struct include GasVal let names x = Format.asprintf "%d" x end) let startgas () = M.top () (* get_int "ana.context.gas_value" *) - let is_exhausted _ = is_any_exhausted + let is_exhausted _ v = v <= 0 (* callee gas = caller gas - 1 *) let callee_gas f v = max 0 (v - 1) From 5ea31d5d9533d5eee5a644237c388e5346f09e41 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 22 Sep 2024 14:16:02 +0200 Subject: [PATCH 359/566] I won the fight with first-class modules --- src/framework/control.ml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/framework/control.ml b/src/framework/control.ml index df57878035..96b7ff41d4 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -18,18 +18,23 @@ let spec_module: (module Spec) Lazy.t = lazy ( let arg_enabled = get_bool "witness.graphml.enabled" || get_bool "exp.arg.enabled" in let termination_enabled = List.mem "termination" (get_string_list "ana.activated") in (* check if loop termination analysis is enabled*) let open Batteries in + let gas () = + let module GasVal = struct + (* Chain lattice has elements [0,n-1], but we want [0,gas_value] *) + let n () = get_int "ana.context.gas_value" + 1 + end + in + if get_string "ana.context.gas_scope" = "global" then + (module GlobalGas(GasVal):Gas) + else + (module PerFunctionGas(GasVal):Gas) + in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in - let module GasVal = struct - (* Chain lattice has elements [0,n-1], but we want [0,gas_value] *) - let n () = get_int "ana.context.gas_value" + 1 - end - in let module S1 = (val (module MCP.MCP2 : Spec) - |> lift (get_int "ana.context.gas_value" >= 0 && get_string "ana.context.gas_scope" = "global") (module ContextGasLifter(GlobalGas(GasVal))) - |> lift (get_int "ana.context.gas_value" >= 0 && get_string "ana.context.gas_scope" = "function") (module ContextGasLifter(PerFunctionGas(GasVal))) + |> lift (get_int "ana.context.gas_value" >= 0) (let (module Gas) = gas () in (module ContextGasLifter(Gas))) |> lift true (module WidenContextLifterSide) (* option checked in functor *) (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) From d421731e55c78505c769247ebeb63f4d75a64b4c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 22 Sep 2024 14:57:00 +0200 Subject: [PATCH 360/566] Comment why `top` is maximal gas. Co-authored-by: Julian Erhard --- src/framework/constraints.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index b9553b974a..5ba9f034c2 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -522,7 +522,7 @@ end module GlobalGas(GasVal:GasVal):Gas = struct module M = Lattice.Chain (struct include GasVal let names x = Format.asprintf "%d" x end) - let startgas () = M.top () (* get_int "ana.context.gas_value" *) + let startgas () = M.top () (* M.top () yields maximal gas value *) let is_exhausted _ v = v <= 0 From 3ee79bf980bee30691884652d76d795c338588c7 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 22 Sep 2024 15:23:07 +0200 Subject: [PATCH 361/566] Embrace dynamic nature of everything --- src/framework/analyses.ml | 2 + src/framework/constraints.ml | 71 ++++++++++++++++++++---------------- src/framework/control.ml | 15 +------- 3 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index bb494382d7..ab41335944 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -274,6 +274,8 @@ sig val event : (D.t, G.t, C.t, V.t) ctx -> Events.t -> (D.t, G.t, C.t, V.t) ctx -> D.t end +module type Spec2Spec = functor (S: Spec) -> Spec + module type MCPA = sig include Printable.S diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 5ba9f034c2..167ad08484 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -508,10 +508,6 @@ end module NoContext = struct let name = "no context" end -module type GasVal = sig - val n: unit -> int -end - module type Gas = sig module M:Lattice.S val startgas: unit -> M.t @@ -520,34 +516,6 @@ module type Gas = sig val thread_gas: varinfo -> M.t -> M.t end -module GlobalGas(GasVal:GasVal):Gas = struct - module M = Lattice.Chain (struct include GasVal let names x = Format.asprintf "%d" x end) - let startgas () = M.top () (* M.top () yields maximal gas value *) - - let is_exhausted _ v = v <= 0 - - (* callee gas = caller gas - 1 *) - let callee_gas f v = max 0 (v - 1) - let thread_gas f v = max 0 (v - 1) -end - -module PerFunctionGas(GasVal:GasVal):Gas = struct - module G = Lattice.Chain (struct include GasVal let names x = Format.asprintf "%d" x end) - module M = MapDomain.MapTop_LiftBot(CilType.Fundec)(G) - let startgas () = M.empty () - let is_exhausted f v = GobOption.exists (fun g -> g <= 0) (M.find_opt f v) (* v <= 0 *) - let callee_gas f v = - let c = Option.default (G.top ()) (M.find_opt f v) in - M.add f (max 0 c-1) v - let thread_gas f v = - match Cilfacade.find_varinfo_fundec f with - | fd -> - callee_gas fd v - | exception Not_found -> - callee_gas Cil.dummyFunDec v -end - - (** Lifts a [Spec] with the context gas variable. The gas variable limits the number of context-sensitively analyzed function calls in a call stack. For every function call the gas is reduced. If the gas is zero, the remaining function calls are analyzed without context-information *) module ContextGasLifter (Gas:Gas) (S:Spec) @@ -631,6 +599,45 @@ struct let event ctx e octx = S.event (conv ctx) e (conv octx), cg_val ctx end +let get_gas_lifter () = + let module GasChain = Lattice.Chain (struct + (* Chain lattice has elements [0,n-1], but we want [0,gas_value] *) + let n () = get_int "ana.context.gas_value" + 1 + let names x = Format.asprintf "%d" x + end) + in + if get_string "ana.context.gas_scope" = "global" then + let module GlobalGas:Gas = struct + module M = GasChain + let startgas () = M.top () (* M.top () yields maximal gas value *) + + let is_exhausted _ v = v <= 0 + + (* callee gas = caller gas - 1 *) + let callee_gas f v = max 0 (v - 1) + let thread_gas f v = max 0 (v - 1) + end + in + (module ContextGasLifter(GlobalGas):Spec2Spec) + else + let module PerFunctionGas:Gas = struct + module G = GasChain + module M = MapDomain.MapTop_LiftBot(CilType.Fundec)(G) + let startgas () = M.empty () + let is_exhausted f v = GobOption.exists (fun g -> g <= 0) (M.find_opt f v) (* v <= 0 *) + let callee_gas f v = + let c = Option.default (G.top ()) (M.find_opt f v) in + M.add f (max 0 c-1) v + let thread_gas f v = + match Cilfacade.find_varinfo_fundec f with + | fd -> + callee_gas fd v + | exception Not_found -> + callee_gas Cil.dummyFunDec v + end + in + (module ContextGasLifter(PerFunctionGas):Spec2Spec) + module type Increment = sig diff --git a/src/framework/control.ml b/src/framework/control.ml index 96b7ff41d4..856695e690 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -10,7 +10,7 @@ open ConstrSys open GobConfig open Constraints -module type S2S = functor (X : Spec) -> Spec +module type S2S = Spec2Spec (* spec is lazy, so HConsed table in Hashcons lifters is preserved between analyses in server mode *) let spec_module: (module Spec) Lazy.t = lazy ( @@ -18,23 +18,12 @@ let spec_module: (module Spec) Lazy.t = lazy ( let arg_enabled = get_bool "witness.graphml.enabled" || get_bool "exp.arg.enabled" in let termination_enabled = List.mem "termination" (get_string_list "ana.activated") in (* check if loop termination analysis is enabled*) let open Batteries in - let gas () = - let module GasVal = struct - (* Chain lattice has elements [0,n-1], but we want [0,gas_value] *) - let n () = get_int "ana.context.gas_value" + 1 - end - in - if get_string "ana.context.gas_scope" = "global" then - (module GlobalGas(GasVal):Gas) - else - (module PerFunctionGas(GasVal):Gas) - in (* apply functor F on module X if opt is true *) let lift opt (module F : S2S) (module X : Spec) = (module (val if opt then (module F (X)) else (module X) : Spec) : Spec) in let module S1 = (val (module MCP.MCP2 : Spec) - |> lift (get_int "ana.context.gas_value" >= 0) (let (module Gas) = gas () in (module ContextGasLifter(Gas))) + |> lift (get_int "ana.context.gas_value" >= 0) (Constraints.get_gas_lifter ()) |> lift true (module WidenContextLifterSide) (* option checked in functor *) (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) From 426ec6964f777e3901952569916dd4eb63295cf3 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 22 Sep 2024 15:31:39 +0200 Subject: [PATCH 362/566] Make `contextGasLifter` its own module --- src/framework/constraints.ml | 133 --------------------------- src/framework/contextGasLifter.ml | 140 +++++++++++++++++++++++++++++ src/framework/contextGasLifter.mli | 1 + src/framework/control.ml | 2 +- 4 files changed, 142 insertions(+), 134 deletions(-) create mode 100644 src/framework/contextGasLifter.ml create mode 100644 src/framework/contextGasLifter.mli diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 167ad08484..23cc297439 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -506,139 +506,6 @@ struct let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot end -module NoContext = struct let name = "no context" end - -module type Gas = sig - module M:Lattice.S - val startgas: unit -> M.t - val is_exhausted: fundec -> M.t -> bool - val callee_gas: fundec -> M.t -> M.t - val thread_gas: varinfo -> M.t -> M.t -end - -(** Lifts a [Spec] with the context gas variable. The gas variable limits the number of context-sensitively analyzed function calls in a call stack. - For every function call the gas is reduced. If the gas is zero, the remaining function calls are analyzed without context-information *) -module ContextGasLifter (Gas:Gas) (S:Spec) - : Spec with module D = Lattice.Prod (S.D) (Gas.M) - and module C = Printable.Option (S.C) (NoContext) - and module G = S.G -= -struct - include S - - module Context_Gas_Prod (Base1: Lattice.S) (Base2: Lattice.S) = - struct - include Lattice.Prod (Base1) (Base2) - let printXml f (x,y) = - BatPrintf.fprintf f "\n%a\n%a\n" Base1.printXml x Base2.printXml y - end - module D = Context_Gas_Prod (S.D) (Gas.M) (* Product of S.D and an integer, tracking the context gas value *) - module C = Printable.Option (S.C) (NoContext) - module G = S.G - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - (* returns context gas value of the given ctx *) - let cg_val ctx = snd ctx.local - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - - let startcontext () = Some (S.startcontext ()) - let name () = S.name ()^" with context gas" - let startstate v = S.startstate v, Gas.startgas () - let exitstate v = S.exitstate v, Gas.startgas () - let morphstate v (d,i) = S.morphstate v d, i - - let conv (ctx:(D.t,G.t,C.t,V.t) ctx): (S.D.t,G.t,S.C.t,V.t)ctx = - {ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, cg_val ctx) es) - ; context = (fun () -> match ctx.context () with Some c -> c | None -> ctx_failwith "no context (contextGas = 0)")} - - let context ctx fd (d,i) = - (* only keep context if the context gas is greater zero *) - if Gas.is_exhausted fd i then - None - else - Some (S.context (conv ctx) fd d) - - let enter ctx r f args = - let liftmap_tup = List.map (fun (x,y) -> (x, cg_val ctx), (y, Gas.callee_gas f (cg_val ctx))) in - liftmap_tup (S.enter (conv ctx) r f args) - - let threadenter ctx ~multiple lval f args = - let liftmap d = List.map (fun (x) -> (x, Gas.thread_gas f (cg_val ctx))) d in - liftmap (S.threadenter (conv ctx) ~multiple lval f args) - - let query ctx (type a) (q: a Queries.t):a Queries.result = - match q with - | Queries.GasExhausted f -> - let (d,i) = ctx.local in - Gas.is_exhausted f i - | _ -> S.query (conv ctx) q - - let sync ctx reason = S.sync (conv ctx) reason, cg_val ctx - let assign ctx lval expr = S.assign (conv ctx) lval expr, cg_val ctx - let vdecl ctx v = S.vdecl (conv ctx) v, cg_val ctx - let body ctx fundec = S.body (conv ctx) fundec, cg_val ctx - let branch ctx e tv = S.branch (conv ctx) e tv, cg_val ctx - let return ctx r f = S.return (conv ctx) r f, cg_val ctx - let asm ctx = S.asm (conv ctx), cg_val ctx - let skip ctx = S.skip (conv ctx), cg_val ctx - let special ctx r f args = S.special (conv ctx) r f args, cg_val ctx - let combine_env ctx r fe f args fc es f_ask = S.combine_env (conv ctx) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val ctx - let combine_assign ctx r fe f args fc es f_ask = S.combine_assign (conv ctx) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val ctx - let paths_as_set ctx = List.map (fun (x) -> (x, cg_val ctx)) @@ S.paths_as_set (conv ctx) - let threadspawn ctx ~multiple lval f args fctx = S.threadspawn (conv ctx) ~multiple lval f args (conv fctx), cg_val ctx - let event ctx e octx = S.event (conv ctx) e (conv octx), cg_val ctx -end - -let get_gas_lifter () = - let module GasChain = Lattice.Chain (struct - (* Chain lattice has elements [0,n-1], but we want [0,gas_value] *) - let n () = get_int "ana.context.gas_value" + 1 - let names x = Format.asprintf "%d" x - end) - in - if get_string "ana.context.gas_scope" = "global" then - let module GlobalGas:Gas = struct - module M = GasChain - let startgas () = M.top () (* M.top () yields maximal gas value *) - - let is_exhausted _ v = v <= 0 - - (* callee gas = caller gas - 1 *) - let callee_gas f v = max 0 (v - 1) - let thread_gas f v = max 0 (v - 1) - end - in - (module ContextGasLifter(GlobalGas):Spec2Spec) - else - let module PerFunctionGas:Gas = struct - module G = GasChain - module M = MapDomain.MapTop_LiftBot(CilType.Fundec)(G) - let startgas () = M.empty () - let is_exhausted f v = GobOption.exists (fun g -> g <= 0) (M.find_opt f v) (* v <= 0 *) - let callee_gas f v = - let c = Option.default (G.top ()) (M.find_opt f v) in - M.add f (max 0 c-1) v - let thread_gas f v = - match Cilfacade.find_varinfo_fundec f with - | fd -> - callee_gas fd v - | exception Not_found -> - callee_gas Cil.dummyFunDec v - end - in - (module ContextGasLifter(PerFunctionGas):Spec2Spec) - - module type Increment = sig val increment: increment_data option diff --git a/src/framework/contextGasLifter.ml b/src/framework/contextGasLifter.ml new file mode 100644 index 0000000000..155eae2d78 --- /dev/null +++ b/src/framework/contextGasLifter.ml @@ -0,0 +1,140 @@ +open Batteries +open GoblintCil +open MyCFG +open Analyses +open ConstrSys +open GobConfig + +module M = Messages + +module NoContext = struct let name = "no context" end + +module type Gas = sig + module M:Lattice.S + val startgas: unit -> M.t + val is_exhausted: fundec -> M.t -> bool + val callee_gas: fundec -> M.t -> M.t + val thread_gas: varinfo -> M.t -> M.t +end + +(** Lifts a [Spec] with the context gas variable. The gas variable limits the number of context-sensitively analyzed function calls in a call stack. + For every function call the gas is reduced. If the gas is zero, the remaining function calls are analyzed without context-information *) +module ContextGasLifter (Gas:Gas) (S:Spec) + : Spec with module D = Lattice.Prod (S.D) (Gas.M) + and module C = Printable.Option (S.C) (NoContext) + and module G = S.G += +struct + include S + + module Context_Gas_Prod (Base1: Lattice.S) (Base2: Lattice.S) = + struct + include Lattice.Prod (Base1) (Base2) + let printXml f (x,y) = + BatPrintf.fprintf f "\n%a\n%a\n" Base1.printXml x Base2.printXml y + end + module D = Context_Gas_Prod (S.D) (Gas.M) (* Product of S.D and an integer, tracking the context gas value *) + module C = Printable.Option (S.C) (NoContext) + module G = S.G + module V = S.V + module P = + struct + include S.P + let of_elt (x, _) = of_elt x + end + + (* returns context gas value of the given ctx *) + let cg_val ctx = snd ctx.local + + type marshal = S.marshal + let init = S.init + let finalize = S.finalize + + + let startcontext () = Some (S.startcontext ()) + let name () = S.name ()^" with context gas" + let startstate v = S.startstate v, Gas.startgas () + let exitstate v = S.exitstate v, Gas.startgas () + let morphstate v (d,i) = S.morphstate v d, i + + let conv (ctx:(D.t,G.t,C.t,V.t) ctx): (S.D.t,G.t,S.C.t,V.t)ctx = + {ctx with local = fst ctx.local + ; split = (fun d es -> ctx.split (d, cg_val ctx) es) + ; context = (fun () -> match ctx.context () with Some c -> c | None -> ctx_failwith "no context (contextGas = 0)")} + + let context ctx fd (d,i) = + (* only keep context if the context gas is greater zero *) + if Gas.is_exhausted fd i then + None + else + Some (S.context (conv ctx) fd d) + + let enter ctx r f args = + let liftmap_tup = List.map (fun (x,y) -> (x, cg_val ctx), (y, Gas.callee_gas f (cg_val ctx))) in + liftmap_tup (S.enter (conv ctx) r f args) + + let threadenter ctx ~multiple lval f args = + let liftmap d = List.map (fun (x) -> (x, Gas.thread_gas f (cg_val ctx))) d in + liftmap (S.threadenter (conv ctx) ~multiple lval f args) + + let query ctx (type a) (q: a Queries.t):a Queries.result = + match q with + | Queries.GasExhausted f -> + let (d,i) = ctx.local in + Gas.is_exhausted f i + | _ -> S.query (conv ctx) q + + let sync ctx reason = S.sync (conv ctx) reason, cg_val ctx + let assign ctx lval expr = S.assign (conv ctx) lval expr, cg_val ctx + let vdecl ctx v = S.vdecl (conv ctx) v, cg_val ctx + let body ctx fundec = S.body (conv ctx) fundec, cg_val ctx + let branch ctx e tv = S.branch (conv ctx) e tv, cg_val ctx + let return ctx r f = S.return (conv ctx) r f, cg_val ctx + let asm ctx = S.asm (conv ctx), cg_val ctx + let skip ctx = S.skip (conv ctx), cg_val ctx + let special ctx r f args = S.special (conv ctx) r f args, cg_val ctx + let combine_env ctx r fe f args fc es f_ask = S.combine_env (conv ctx) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val ctx + let combine_assign ctx r fe f args fc es f_ask = S.combine_assign (conv ctx) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val ctx + let paths_as_set ctx = List.map (fun (x) -> (x, cg_val ctx)) @@ S.paths_as_set (conv ctx) + let threadspawn ctx ~multiple lval f args fctx = S.threadspawn (conv ctx) ~multiple lval f args (conv fctx), cg_val ctx + let event ctx e octx = S.event (conv ctx) e (conv octx), cg_val ctx +end + +let get_gas_lifter () = + let module GasChain = Lattice.Chain (struct + (* Chain lattice has elements [0,n-1], but we want [0,gas_value] *) + let n () = get_int "ana.context.gas_value" + 1 + let names x = Format.asprintf "%d" x + end) + in + if get_string "ana.context.gas_scope" = "global" then + let module GlobalGas:Gas = struct + module M = GasChain + let startgas () = M.top () (* M.top () yields maximal gas value *) + + let is_exhausted _ v = v <= 0 + + (* callee gas = caller gas - 1 *) + let callee_gas f v = max 0 (v - 1) + let thread_gas f v = max 0 (v - 1) + end + in + (module ContextGasLifter(GlobalGas):Spec2Spec) + else + let module PerFunctionGas:Gas = struct + module G = GasChain + module M = MapDomain.MapTop_LiftBot(CilType.Fundec)(G) + let startgas () = M.empty () + let is_exhausted f v = GobOption.exists (fun g -> g <= 0) (M.find_opt f v) (* v <= 0 *) + let callee_gas f v = + let c = Option.default (G.top ()) (M.find_opt f v) in + M.add f (max 0 c-1) v + let thread_gas f v = + match Cilfacade.find_varinfo_fundec f with + | fd -> + callee_gas fd v + | exception Not_found -> + callee_gas Cil.dummyFunDec v + end + in + (module ContextGasLifter(PerFunctionGas):Spec2Spec) diff --git a/src/framework/contextGasLifter.mli b/src/framework/contextGasLifter.mli new file mode 100644 index 0000000000..4f8ce4fb72 --- /dev/null +++ b/src/framework/contextGasLifter.mli @@ -0,0 +1 @@ +val get_gas_lifter : unit -> (module Analyses.Spec2Spec) diff --git a/src/framework/control.ml b/src/framework/control.ml index 856695e690..7ea144e1d1 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -23,7 +23,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( let module S1 = (val (module MCP.MCP2 : Spec) - |> lift (get_int "ana.context.gas_value" >= 0) (Constraints.get_gas_lifter ()) + |> lift (get_int "ana.context.gas_value" >= 0) (ContextGasLifter.get_gas_lifter ()) |> lift true (module WidenContextLifterSide) (* option checked in functor *) (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) From 7112baffc61ce6b4883ab409f8734bba34594f97 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 22 Sep 2024 15:35:11 +0200 Subject: [PATCH 363/566] Document context gas lifter --- src/framework/contextGasLifter.ml | 5 +++-- src/goblint_lib.ml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/framework/contextGasLifter.ml b/src/framework/contextGasLifter.ml index 155eae2d78..6a991b1f58 100644 --- a/src/framework/contextGasLifter.ml +++ b/src/framework/contextGasLifter.ml @@ -1,3 +1,6 @@ +(** Lifts a [Spec] with the context gas variable. The gas variable limits the number of context-sensitively analyzed function calls in a call stack. + For every function call the gas is reduced. If the gas is zero, the remaining function calls are analyzed without context-information *) + open Batteries open GoblintCil open MyCFG @@ -17,8 +20,6 @@ module type Gas = sig val thread_gas: varinfo -> M.t -> M.t end -(** Lifts a [Spec] with the context gas variable. The gas variable limits the number of context-sensitively analyzed function calls in a call stack. - For every function call the gas is reduced. If the gas is zero, the remaining function calls are analyzed without context-information *) module ContextGasLifter (Gas:Gas) (S:Spec) : Spec with module D = Lattice.Prod (S.D) (Gas.M) and module C = Printable.Option (S.C) (NoContext) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 8013d9b2fe..1743e87bea 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -26,6 +26,7 @@ module Constraints = Constraints module AnalysisState = AnalysisState module AnalysisStateUtil = AnalysisStateUtil module ControlSpecC = ControlSpecC +module ContextGasLifter = ContextGasLifter (** Master control program (MCP) is the analysis specification for the dynamic product of activated analyses. *) From d76e40ec0312fa044318a41eea398efe4ba01830 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 23 Sep 2024 10:45:41 +0200 Subject: [PATCH 364/566] Duplicate module synopsis --- src/framework/contextGasLifter.mli | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/framework/contextGasLifter.mli b/src/framework/contextGasLifter.mli index 4f8ce4fb72..f5b07e2b35 100644 --- a/src/framework/contextGasLifter.mli +++ b/src/framework/contextGasLifter.mli @@ -1 +1,5 @@ +(** Lifts a [Spec] with the context gas variable. The gas variable limits the number of context-sensitively analyzed function calls in a call stack. + For every function call the gas is reduced. If the gas is zero, the remaining function calls are analyzed without context-information *) + +(** Gets the appropriate lifter (either local or per-function). Should only be called when context gas is active. *) val get_gas_lifter : unit -> (module Analyses.Spec2Spec) From c4c9c89e0446ff2619deb961525583f4d37e4f44 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 23 Sep 2024 10:48:11 +0200 Subject: [PATCH 365/566] Update outdated comment --- src/framework/contextGasLifter.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/framework/contextGasLifter.ml b/src/framework/contextGasLifter.ml index 6a991b1f58..adb55aa7a2 100644 --- a/src/framework/contextGasLifter.ml +++ b/src/framework/contextGasLifter.ml @@ -34,7 +34,7 @@ struct let printXml f (x,y) = BatPrintf.fprintf f "\n%a\n%a\n" Base1.printXml x Base2.printXml y end - module D = Context_Gas_Prod (S.D) (Gas.M) (* Product of S.D and an integer, tracking the context gas value *) + module D = Context_Gas_Prod (S.D) (Gas.M) (* Product of S.D and a value from the gas module, tracking the context gas value *) module C = Printable.Option (S.C) (NoContext) module G = S.G module V = S.V From 39686d54226267517da7c63361eb906f14b6ee5b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 23 Sep 2024 12:05:00 +0300 Subject: [PATCH 366/566] Handle BotValue/TopValue in Lattice lifters (closes #1572) --- src/domain/lattice.ml | 68 ++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/src/domain/lattice.ml b/src/domain/lattice.ml index f29cb8217d..cc72dd5be0 100644 --- a/src/domain/lattice.ml +++ b/src/domain/lattice.ml @@ -93,10 +93,10 @@ struct include Base let leq = equal let join x y = - if equal x y then x else raise (Unsupported "fake join") + if equal x y then x else raise (Unsupported "fake join") (* TODO: TopValue? *) let widen = join let meet x y = - if equal x y then x else raise (Unsupported "fake meet") + if equal x y then x else raise (Unsupported "fake meet") (* TODO: BotValue? *) let narrow = meet include NoBotTop @@ -259,7 +259,9 @@ struct | (_, `Top) -> `Top | (`Bot, x) -> x | (x, `Bot) -> x - | (`Lifted x, `Lifted y) -> `Lifted (Base.join x y) + | (`Lifted x, `Lifted y) -> + try `Lifted (Base.join x y) + with TopValue -> `Top let meet x y = match (x,y) with @@ -267,16 +269,26 @@ struct | (_, `Bot) -> `Bot | (`Top, x) -> x | (x, `Top) -> x - | (`Lifted x, `Lifted y) -> `Lifted (Base.meet x y) + | (`Lifted x, `Lifted y) -> + try `Lifted (Base.meet x y) + with BotValue -> `Bot let widen x y = match (x,y) with - | (`Lifted x, `Lifted y) -> `Lifted (Base.widen x y) + | (`Lifted x, `Lifted y) -> + begin + try `Lifted (Base.widen x y) + with TopValue -> `Top + end | _ -> y let narrow x y = match (x,y) with - | (`Lifted x, `Lifted y) -> `Lifted (Base.narrow x y) + | (`Lifted x, `Lifted y) -> + begin + try `Lifted (Base.narrow x y) + with BotValue -> `Bot + end | (_, `Bot) -> `Bot | (`Top, y) -> y | _ -> x @@ -315,7 +327,7 @@ struct | (x, `Bot) -> x | (`Lifted x, `Lifted y) -> try `Lifted (Base.join x y) - with Uncomparable -> `Top + with Uncomparable | TopValue -> `Top let meet x y = match (x,y) with @@ -325,20 +337,24 @@ struct | (x, `Top) -> x | (`Lifted x, `Lifted y) -> try `Lifted (Base.meet x y) - with Uncomparable -> `Bot + with Uncomparable | BotValue -> `Bot let widen x y = match (x,y) with | (`Lifted x, `Lifted y) -> - (try `Lifted (Base.widen x y) - with Uncomparable -> `Top) + begin + try `Lifted (Base.widen x y) + with Uncomparable | TopValue -> `Top + end | _ -> y let narrow x y = match (x,y) with | (`Lifted x, `Lifted y) -> - (try `Lifted (Base.narrow x y) - with Uncomparable -> `Bot) + begin + try `Lifted (Base.narrow x y) + with Uncomparable | BotValue -> `Bot + end | (_, `Bot) -> `Bot | (`Top, y) -> y | _ -> x @@ -378,11 +394,11 @@ struct | (x, `Bot) -> x | (`Lifted1 x, `Lifted1 y) -> begin try `Lifted1 (Base1.join x y) - with Unsupported _ -> `Top + with Unsupported _ | TopValue -> `Top end | (`Lifted2 x, `Lifted2 y) -> begin try `Lifted2 (Base2.join x y) - with Unsupported _ -> `Top + with Unsupported _ | TopValue -> `Top end | _ -> `Top @@ -394,11 +410,11 @@ struct | (x, `Top) -> x | (`Lifted1 x, `Lifted1 y) -> begin try `Lifted1 (Base1.meet x y) - with Unsupported _ -> `Bot + with Unsupported _ | BotValue -> `Bot end | (`Lifted2 x, `Lifted2 y) -> begin try `Lifted2 (Base2.meet x y) - with Unsupported _ -> `Bot + with Unsupported _ | BotValue -> `Bot end | _ -> `Bot @@ -489,7 +505,9 @@ struct match (x,y) with | (`Bot, _) -> `Bot | (_, `Bot) -> `Bot - | (`Lifted x, `Lifted y) -> `Lifted (Base.meet x y) + | (`Lifted x, `Lifted y) -> + try `Lifted (Base.meet x y) + with BotValue -> `Bot let widen x y = match (x,y) with @@ -498,7 +516,11 @@ struct let narrow x y = match (x,y) with - | (`Lifted x, `Lifted y) -> `Lifted (Base.narrow x y) + | (`Lifted x, `Lifted y) -> + begin + try `Lifted (Base.narrow x y) + with BotValue -> `Bot + end | (_, `Bot) -> `Bot | _ -> x end @@ -525,7 +547,9 @@ struct match (x,y) with | (`Top, x) -> `Top | (x, `Top) -> `Top - | (`Lifted x, `Lifted y) -> `Lifted (Base.join x y) + | (`Lifted x, `Lifted y) -> + try `Lifted (Base.join x y) + with TopValue -> `Top let meet x y = match (x,y) with @@ -535,7 +559,11 @@ struct let widen x y = match (x,y) with - | (`Lifted x, `Lifted y) -> `Lifted (Base.widen x y) + | (`Lifted x, `Lifted y) -> + begin + try `Lifted (Base.widen x y) + with TopValue -> `Top + end | _ -> y let narrow x y = From 372a838f4690760719ee79adc113e550715b92de Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 23 Sep 2024 12:27:49 +0300 Subject: [PATCH 367/566] Remove Lattice.Unsupported All cases can be replaced with BotValue or TopValue. --- src/cdomain/value/cdomains/stringDomain.ml | 2 +- src/cdomains/stackDomain.ml | 4 ++-- src/domain/lattice.ml | 20 +++++++------------- src/domain/mapDomain.ml | 4 ++-- src/domains/domainProperties.ml | 2 +- tests/unit/domains/mapDomainTest.ml | 4 ++-- 6 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/cdomain/value/cdomains/stringDomain.ml b/src/cdomain/value/cdomains/stringDomain.ml index 2b968b0321..8ab5cd384a 100644 --- a/src/cdomain/value/cdomains/stringDomain.ml +++ b/src/cdomain/value/cdomains/stringDomain.ml @@ -72,7 +72,7 @@ let to_string_length x = let to_exp = function | Some x -> GoblintCil.mkString x - | None -> raise (Lattice.Unsupported "Cannot express unknown string pointer as expression.") + | None -> failwith "Cannot express unknown string pointer as expression." let semantic_equal x y = match x, y with diff --git a/src/cdomains/stackDomain.ml b/src/cdomains/stackDomain.ml index 50864d6294..5a5168e22c 100644 --- a/src/cdomains/stackDomain.ml +++ b/src/cdomains/stackDomain.ml @@ -13,14 +13,14 @@ struct let n = 3 let rec times x = function 0 -> [] | n -> x::times x (n-1) let rec map2 f xs ys = - match xs, ys with x::xs, y::ys -> (try f x y :: map2 f xs ys with Lattice.Unsupported _ -> []) | _ -> [] + match xs, ys with x::xs, y::ys -> (try f x y :: map2 f xs ys with Lattice.BotValue | Lattice.TopValue -> []) | _ -> [] let rec fold_left2 f a b xs ys = match xs, ys with | [], _ | _, [] -> a | x::xs, y::ys -> try fold_left2 f (f a x y) b xs ys with - | Lattice.Unsupported _ -> b + | Lattice.BotValue | Lattice.TopValue -> b let rec take n xs = match n, xs with diff --git a/src/domain/lattice.ml b/src/domain/lattice.ml index cc72dd5be0..71a130573f 100644 --- a/src/domain/lattice.ml +++ b/src/domain/lattice.ml @@ -45,9 +45,6 @@ exception BotValue (** Exception raised by a bottomless lattice in place of a bottom value. Surrounding lattice functors may handle this on their own. *) -exception Unsupported of string -let unsupported x = raise (Unsupported x) - exception Invalid_widen of Pretty.doc let () = Printexc.register_printer (function @@ -93,10 +90,10 @@ struct include Base let leq = equal let join x y = - if equal x y then x else raise (Unsupported "fake join") (* TODO: TopValue? *) + if equal x y then x else raise TopValue let widen = join let meet x y = - if equal x y then x else raise (Unsupported "fake meet") (* TODO: BotValue? *) + if equal x y then x else raise BotValue let narrow = meet include NoBotTop @@ -394,11 +391,11 @@ struct | (x, `Bot) -> x | (`Lifted1 x, `Lifted1 y) -> begin try `Lifted1 (Base1.join x y) - with Unsupported _ | TopValue -> `Top + with TopValue -> `Top end | (`Lifted2 x, `Lifted2 y) -> begin try `Lifted2 (Base2.join x y) - with Unsupported _ | TopValue -> `Top + with TopValue -> `Top end | _ -> `Top @@ -410,11 +407,11 @@ struct | (x, `Top) -> x | (`Lifted1 x, `Lifted1 y) -> begin try `Lifted1 (Base1.meet x y) - with Unsupported _ | BotValue -> `Bot + with BotValue -> `Bot end | (`Lifted2 x, `Lifted2 y) -> begin try `Lifted2 (Base2.meet x y) - with Unsupported _ | BotValue -> `Bot + with BotValue -> `Bot end | _ -> `Bot @@ -581,10 +578,7 @@ end module Liszt (Base: S) = struct include Printable.Liszt (Base) - let bot () = raise (Unsupported "bot?") - let is_top _ = false - let top () = raise (Unsupported "top?") - let is_bot _ = false + include NoBotTop let leq = let f acc x y = Base.leq x y && acc in diff --git a/src/domain/mapDomain.ml b/src/domain/mapDomain.ml index a62fcb98e4..033302d8df 100644 --- a/src/domain/mapDomain.ml +++ b/src/domain/mapDomain.ml @@ -398,7 +398,7 @@ struct let leq = leq_with_fct Range.leq let find x m = try find x m with | Not_found -> Range.bot () - let top () = Lattice.unsupported "partial map top" + let top () = raise Lattice.TopValue let bot () = empty () let is_top _ = false let is_bot = is_empty @@ -448,7 +448,7 @@ struct let find x m = try find x m with | Not_found -> Range.top () let top () = empty () - let bot () = Lattice.unsupported "partial map bot" + let bot () = raise Lattice.BotValue let is_top = is_empty let is_bot _ = false diff --git a/src/domains/domainProperties.ml b/src/domains/domainProperties.ml index b2f0f7671a..fdf8e7512c 100644 --- a/src/domains/domainProperties.ml +++ b/src/domains/domainProperties.ml @@ -126,7 +126,7 @@ struct with | Failure _ (* raised by IntDomain *) | SetDomain.Unsupported _ (* raised by SetDomain *) - | Lattice.Unsupported _ (* raised by MapDomain *) -> + | Lattice.TopValue (* raised by MapDomain *) -> false let top_leq = make ~name:"top leq" (arb) (fun a -> diff --git a/tests/unit/domains/mapDomainTest.ml b/tests/unit/domains/mapDomainTest.ml index 26b8b6725c..64be44f77e 100644 --- a/tests/unit/domains/mapDomainTest.ml +++ b/tests/unit/domains/mapDomainTest.ml @@ -18,7 +18,7 @@ struct let get_empty () = try (is_empty_top := true; M.top ()) - with Lattice.Unsupported _ | Lattice.BotValue | Lattice.TopValue -> + with Lattice.TopValue -> (is_empty_top := false; M.bot ()) let is_empty x = @@ -44,7 +44,7 @@ struct map := M.remove "key1" !map; begin try ignore (M.find "key1" !map); assert_failure "problem removeing key1" - with Lattice.Unsupported _ | Lattice.BotValue | Lattice.TopValue -> () + with Lattice.BotValue | Lattice.TopValue -> () end ; end From 82d580f5214fec7dfac8b9fa5bf2576181837998 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 11:38:32 +0300 Subject: [PATCH 368/566] Pin CIL with 32bit and 64bit Machdeps --- goblint.opam | 2 +- goblint.opam.locked | 2 +- goblint.opam.template | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/goblint.opam b/goblint.opam index 565d1fab5b..0346e78252 100644 --- a/goblint.opam +++ b/goblint.opam @@ -97,7 +97,7 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#04b8a45a7d20425c7b6c8abe1ad094abc063922b" ] + [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#73d02511a0366816d428853634fb939bd2f0a1b7" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.locked b/goblint.opam.locked index 28dd8e17e9..dcf81b7a53 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -140,7 +140,7 @@ post-messages: [ pin-depends: [ [ "goblint-cil.2.0.4" - "git+https://github.com/goblint/cil.git#04b8a45a7d20425c7b6c8abe1ad094abc063922b" + "git+https://github.com/goblint/cil.git#73d02511a0366816d428853634fb939bd2f0a1b7" ] ] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.template b/goblint.opam.template index 9f29ceb7a5..2a5d5690fc 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -2,7 +2,7 @@ # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#04b8a45a7d20425c7b6c8abe1ad094abc063922b" ] + [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#73d02511a0366816d428853634fb939bd2f0a1b7" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} From 917cf7e9387994ee79c5c156d5ded71f21361cf7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 11:38:53 +0300 Subject: [PATCH 369/566] Change Machdep based on SV-COMP architecture --- src/common/util/cilfacade.ml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index d520d250e2..a23ee64149 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -47,7 +47,16 @@ let init_options () = Mergecil.merge_inlines := get_bool "cil.merge.inlines"; Cil.cstd := Cil.cstd_of_string (get_string "cil.cstd"); Cil.gnu89inline := get_bool "cil.gnu89inline"; - Cabs2cil.addNestedScopeAttr := get_bool "cil.addNestedScopeAttr" + Cabs2cil.addNestedScopeAttr := get_bool "cil.addNestedScopeAttr"; + + if get_bool "ana.sv-comp.enabled" then ( + let machine = match get_string "exp.architecture" with + | "32bit" -> Machdep.gcc32 + | "64bit" -> Machdep.gcc64 + | _ -> assert false + in + Machdep.theMachine := Option.get machine + ) let init () = initCIL (); From 84e738bf7d675a226c820886b90d56bc78b44877 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 11:55:50 +0300 Subject: [PATCH 370/566] Initialize CIL after initializing CIL options initCIL already depends on theMachine, but it overwrites it. Must use envMachine to select it based on exp.architecture beforehand. --- src/common/util/cilfacade.ml | 4 +--- src/goblint.ml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index a23ee64149..a71f2544e3 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -50,12 +50,10 @@ let init_options () = Cabs2cil.addNestedScopeAttr := get_bool "cil.addNestedScopeAttr"; if get_bool "ana.sv-comp.enabled" then ( - let machine = match get_string "exp.architecture" with + Cil.envMachine := match get_string "exp.architecture" with | "32bit" -> Machdep.gcc32 | "64bit" -> Machdep.gcc64 | _ -> assert false - in - Machdep.theMachine := Option.get machine ) let init () = diff --git a/src/goblint.ml b/src/goblint.ml index 52b9bbdfc0..2707e6e57d 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -5,8 +5,8 @@ open Maingoblint (** the main function *) let main () = try - Cilfacade.init (); Maingoblint.parse_arguments (); + Cilfacade.init (); (* Timing. *) Maingoblint.reset_stats (); From d4b4291355fcdc1e89dd285dea957cf490cf092d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 11:56:34 +0300 Subject: [PATCH 371/566] Eta-expand iDtoIdx in base to prevent ptrdiff_ikind lookup during initialization --- src/analyses/base.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a69b3a2b23..bca66e16af 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -171,7 +171,7 @@ struct * Abstract evaluation functions **************************************************************************) - let iDtoIdx = ID.cast_to (Cilfacade.ptrdiff_ikind ()) + let iDtoIdx x = ID.cast_to (Cilfacade.ptrdiff_ikind ()) x let unop_ID = function | Neg -> ID.neg From 56e687ee21ccee9a4cb3401b77f3f847df0c2f58 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 14:55:04 +0300 Subject: [PATCH 372/566] Rename Constraints -> CompareConstraints for split --- .../{constraints.ml => compareConstraints.ml} | 0 src/framework/control.ml | 10 +++++----- src/goblint_lib.ml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/framework/{constraints.ml => compareConstraints.ml} (100%) diff --git a/src/framework/constraints.ml b/src/framework/compareConstraints.ml similarity index 100% rename from src/framework/constraints.ml rename to src/framework/compareConstraints.ml diff --git a/src/framework/control.ml b/src/framework/control.ml index 7ea144e1d1..620c5575d2 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -8,7 +8,7 @@ open MyCFG open Analyses open ConstrSys open GobConfig -open Constraints +open CompareConstraints module type S2S = Spec2Spec @@ -90,7 +90,7 @@ struct end module Slvr = (GlobSolverFromEqSolver (Goblint_solver.Selector.Make (PostSolverArg))) (EQSys) (LHT) (GHT) (* The comparator *) - module CompareGlobSys = Constraints.CompareGlobSys (SpecSys) + module CompareGlobSys = CompareConstraints.CompareGlobSys (SpecSys) (* Triple of the function, context, and the local value. *) module RT = AnalysisResult.ResultType2 (Spec) @@ -521,15 +521,15 @@ struct if get_bool "dbg.compare_runs.globsys" then CompareGlobSys.compare (d1, d2) r1 r2; - let module CompareEqSys = Constraints.CompareEqSys (S2) (VH) in + let module CompareEqSys = CompareConstraints.CompareEqSys (S2) (VH) in if get_bool "dbg.compare_runs.eqsys" then CompareEqSys.compare (d1, d2) r1' r2'; - let module CompareGlobal = Constraints.CompareGlobal (EQSys.GVar) (EQSys.G) (GHT) in + let module CompareGlobal = CompareConstraints.CompareGlobal (EQSys.GVar) (EQSys.G) (GHT) in if get_bool "dbg.compare_runs.global" then CompareGlobal.compare (d1, d2) (snd r1) (snd r2); - let module CompareNode = Constraints.CompareNode (Spec.C) (EQSys.D) (LHT) in + let module CompareNode = CompareConstraints.CompareNode (Spec.C) (EQSys.D) (LHT) in if get_bool "dbg.compare_runs.node" then CompareNode.compare (d1, d2) (fst r1) (fst r2); diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 1743e87bea..dcce048717 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -22,7 +22,7 @@ module CfgTools = CfgTools module Analyses = Analyses module ConstrSys = ConstrSys -module Constraints = Constraints +module CompareConstraints = CompareConstraints module AnalysisState = AnalysisState module AnalysisStateUtil = AnalysisStateUtil module ControlSpecC = ControlSpecC From ddd4b769bf43fdf9896d0f4839f915561b89c53f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 14:57:02 +0300 Subject: [PATCH 373/566] Remove non-compare modules from CompareConstraints --- src/framework/compareConstraints.ml | 1705 --------------------------- 1 file changed, 1705 deletions(-) diff --git a/src/framework/compareConstraints.ml b/src/framework/compareConstraints.ml index 23cc297439..5197ac8e4e 100644 --- a/src/framework/compareConstraints.ml +++ b/src/framework/compareConstraints.ml @@ -1,1713 +1,8 @@ -(** Construction of a {{!Analyses.MonSystem} constraint system} from an {{!Analyses.Spec} analysis specification} and {{!MyCFG.CfgBackward} CFGs}. - Transformatons of analysis specifications as functors. *) - open Batteries -open GoblintCil -open MyCFG open Analyses open ConstrSys open GobConfig -module M = Messages - - -(** Lifts a [Spec] so that the domain is [Hashcons]d *) -module HashconsLifter (S:Spec) - : Spec with module G = S.G - and module C = S.C -= -struct - module HConsedArg = - struct - (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) - (* see https://github.com/goblint/analyzer/issues/1005 *) - let assume_idempotent = GobConfig.get_string "ana.int.refinement" = "never" - end - module D = Lattice.HConsed (S.D) (HConsedArg) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt x = of_elt (D.unlift x) - end - - let name () = S.name () ^" hashconsed" - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init = S.init - let finalize = S.finalize - - let startstate v = D.lift (S.startstate v) - let exitstate v = D.lift (S.exitstate v) - let morphstate v d = D.lift (S.morphstate v (D.unlift d)) - - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) - } - - let context ctx fd = S.context (conv ctx) fd % D.unlift - let startcontext () = S.startcontext () - - let sync ctx reason = - D.lift @@ S.sync (conv ctx) reason - - let query ctx = - S.query (conv ctx) - - let assign ctx lv e = - D.lift @@ S.assign (conv ctx) lv e - - let vdecl ctx v = - D.lift @@ S.vdecl (conv ctx) v - - let branch ctx e tv = - D.lift @@ S.branch (conv ctx) e tv - - let body ctx f = - D.lift @@ S.body (conv ctx) f - - let return ctx r f = - D.lift @@ S.return (conv ctx) r f - - let asm ctx = - D.lift @@ S.asm (conv ctx) - - let skip ctx = - D.lift @@ S.skip (conv ctx) - - let enter ctx r f args = - List.map (fun (x,y) -> D.lift x, D.lift y) @@ S.enter (conv ctx) r f args - - let special ctx r f args = - D.lift @@ S.special (conv ctx) r f args - - let combine_env ctx r fe f args fc es f_ask = - D.lift @@ S.combine_env (conv ctx) r fe f args fc (D.unlift es) f_ask - - let combine_assign ctx r fe f args fc es f_ask = - D.lift @@ S.combine_assign (conv ctx) r fe f args fc (D.unlift es) f_ask - - let threadenter ctx ~multiple lval f args = - List.map D.lift @@ S.threadenter (conv ctx) ~multiple lval f args - - let threadspawn ctx ~multiple lval f args fctx = - D.lift @@ S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) - - let paths_as_set ctx = - List.map (fun x -> D.lift x) @@ S.paths_as_set (conv ctx) - - let event ctx e octx = - D.lift @@ S.event (conv ctx) e (conv octx) -end - -(** Lifts a [Spec] so that the context is [Hashcons]d. *) -module HashconsContextLifter (S:Spec) - : Spec with module D = S.D - and module G = S.G - and module C = Printable.HConsed (S.C) -= -struct - module D = S.D - module G = S.G - module C = Printable.HConsed (S.C) - module V = S.V - module P = S.P - - let name () = S.name () ^" context hashconsed" - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init = S.init - let finalize = S.finalize - - let startstate = S.startstate - let exitstate = S.exitstate - let morphstate = S.morphstate - - let conv ctx = - { ctx with context = (fun () -> C.unlift (ctx.context ())) } - - let context ctx fd = C.lift % S.context (conv ctx) fd - let startcontext () = C.lift @@ S.startcontext () - - let sync ctx reason = - S.sync (conv ctx) reason - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | Queries.IterPrevVars f -> - let g i (n, c, j) e = f i (n, Obj.repr (C.lift (Obj.obj c)), j) e in - S.query (conv ctx) (Queries.IterPrevVars g) - | _ -> S.query (conv ctx) q - - let assign ctx lv e = - S.assign (conv ctx) lv e - - let vdecl ctx v = - S.vdecl (conv ctx) v - - let branch ctx e tv = - S.branch (conv ctx) e tv - - let body ctx f = - S.body (conv ctx) f - - let return ctx r f = - S.return (conv ctx) r f - - let asm ctx = - S.asm (conv ctx) - - let skip ctx = - S.skip (conv ctx) - - let enter ctx r f args = - S.enter (conv ctx) r f args - - let special ctx r f args = - S.special (conv ctx) r f args - - let combine_env ctx r fe f args fc es f_ask = - S.combine_env (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - - let combine_assign ctx r fe f args fc es f_ask = - S.combine_assign (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - - let threadenter ctx ~multiple lval f args = - S.threadenter (conv ctx) ~multiple lval f args - - let threadspawn ctx ~multiple lval f args fctx = - S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) - - let paths_as_set ctx = S.paths_as_set (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - -(* see option ana.opt.equal *) -module OptEqual (S: Spec) = struct - module D = struct include S.D let equal x y = x == y || equal x y end - module G = struct include S.G let equal x y = x == y || equal x y end - module C = struct include S.C let equal x y = x == y || equal x y end - include (S : Spec with module D := D and module G := G and module C := C) -end - -(** If dbg.slice.on, stops entering functions after dbg.slice.n levels. *) -module LevelSliceLifter (S:Spec) - : Spec with module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) - and module G = S.G - and module C = S.C -= -struct - module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - let name () = S.name ()^" level sliced" - - let start_level = ref (`Top) - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init marshal = - if get_bool "dbg.slice.on" then - start_level := `Lifted (Int64.of_int (get_int "dbg.slice.n")); - S.init marshal - - let finalize = S.finalize - - let startstate v = (S.startstate v, !start_level) - let exitstate v = (S.exitstate v, !start_level) - let morphstate v (d,l) = (S.morphstate v d, l) - - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) - } - - let context ctx fd (d,_) = S.context (conv ctx) fd d - let startcontext () = S.startcontext () - - let lift_fun ctx f g h = - f @@ h (g (conv ctx)) - - let enter' ctx r f args = - let liftmap = List.map (fun (x,y) -> (x, snd ctx.local), (y, snd ctx.local)) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) - - let lift ctx d = (d, snd ctx.local) - let lift_start_level d = (d, !start_level) - - let sync ctx reason = lift_fun ctx (lift ctx) S.sync ((|>) reason) - let query' ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun x -> x q) - let assign ctx lv e = lift_fun ctx (lift ctx) S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx (lift ctx) S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx (lift ctx) S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx (lift ctx) S.body ((|>) f) - let return ctx r f = lift_fun ctx (lift ctx) S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx (lift ctx) S.asm identity - let skip ctx = lift_fun ctx (lift ctx) S.skip identity - let special ctx r f args = lift_fun ctx (lift ctx) S.special ((|>) args % (|>) f % (|>) r) - let combine_env' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (lift ctx) (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - - let leq0 = function - | `Top -> false - | `Lifted x -> x <= 0L - | `Bot -> true - - let sub1 = function - | `Lifted x -> `Lifted (Int64.sub x 1L) - | x -> x - - let add1 = function - | `Lifted x -> `Lifted (Int64.add x 1L) - | x -> x - - let paths_as_set ctx = - let liftmap = List.map (fun x -> (x, snd ctx.local)) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) - - let event ctx e octx = - lift_fun ctx (lift ctx) S.event ((|>) (conv octx) % (|>) e) - - let enter ctx r f args = - let (d,l) = ctx.local in - if leq0 l then - [ctx.local, D.bot ()] - else - enter' {ctx with local=(d, sub1 l)} r f args - - let combine_env ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in - let l = add1 l in - if leq0 l then - (d, l) - else - let d',_ = combine_env' ctx r fe f args fc es f_ask in - (d', l) - - let combine_assign ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in - (* No need to add1 here, already done in combine_env. *) - if leq0 l then - (d, l) - else - let d',_ = combine_assign' ctx r fe f args fc es f_ask in - (d', l) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | Queries.EvalFunvar e -> - let (d,l) = ctx.local in - if leq0 l then - Queries.AD.empty () - else - query' ctx (Queries.EvalFunvar e) - | q -> query' ctx q -end - - -(** Limits the number of widenings per node. *) -module LimitLifter (S:Spec) = -struct - include (S : module type of S with module D := S.D and type marshal = S.marshal) - - let name () = S.name ()^" limited" - - let limit = ref 0 - - let init marshal = - limit := get_int "dbg.limit.widen"; - S.init marshal - - module H = MyCFG.NodeH - let h = H.create 13 - let incr k = - H.modify_def 1 k (fun v -> - if v >= !limit then failwith (GobPretty.sprintf "LimitLifter: Reached limit (%d) for node %a" !limit Node.pretty_plain_short (Option.get !MyCFG.current_node)); - v+1 - ) h; - module D = struct - include S.D - let widen x y = Option.may incr !MyCFG.current_node; widen x y (* when is this None? *) - end -end - - -(* widening on contexts, keeps contexts for calls only in D *) -module WidenContextLifterSide (S:Spec) -= -struct - module DD = - struct - include S.D - let printXml f d = BatPrintf.fprintf f "%a" printXml d - end - module M = MapDomain.MapBot (Basetype.Variables) (DD) (* should be CilFun -> S.C, but CilFun is not Groupable, and S.C is no Lattice *) - - module D = struct - include Lattice.Prod (S.D) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.D.printXml d M.printXml m - end - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - - let name () = S.name ()^" with widened contexts" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - let inj f x = f x, M.bot () - - let startcontext () = S.startcontext () - let startstate = inj S.startstate - let exitstate = inj S.exitstate - let morphstate v (d,m) = S.morphstate v d, m - - - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) - } - - let context ctx fd (d,m) = S.context (conv ctx) fd d (* just the child analysis' context *) - - let lift_fun ctx f g = g (f (conv ctx)), snd ctx.local - - let sync ctx reason = lift_fun ctx S.sync ((|>) reason) - let query ctx = S.query (conv ctx) - let assign ctx lv e = lift_fun ctx S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx S.body ((|>) f) - let return ctx r f = lift_fun ctx S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx S.asm identity - let skip ctx = lift_fun ctx S.skip identity - let special ctx r f args = lift_fun ctx S.special ((|>) args % (|>) f % (|>) r) - - let event ctx e octx = lift_fun ctx S.event ((|>) (conv octx) % (|>) e) - - let threadenter ctx ~multiple lval f args = S.threadenter (conv ctx) ~multiple lval f args |> List.map (fun d -> (d, snd ctx.local)) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - - let enter ctx r f args = - let m = snd ctx.local in - let d' v_cur = - if ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.context.widen" ~keepAttr:"widen" ~removeAttr:"no-widen" f then ( - let v_old = M.find f.svar m in (* S.D.bot () if not found *) - let v_new = S.D.widen v_old (S.D.join v_old v_cur) in - Messages.(if tracing && not (S.D.equal v_old v_new) then tracel "widen-context" "enter results in new context for function %s" f.svar.vname); - v_new, M.add f.svar v_new m - ) - else - v_cur, m - in - S.enter (conv ctx) r f args - |> List.map (fun (c,v) -> (c,m), d' v) (* c: caller, v: callee *) - - let paths_as_set ctx = - let m = snd ctx.local in - S.paths_as_set (conv ctx) |> List.map (fun v -> (v,m)) - - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) -end - - -(** Lifts a [Spec] with a special bottom element that represent unreachable code. *) -module DeadCodeLifter (S:Spec) - : Spec with module D = Dom (S.D) - and module G = S.G - and module C = S.C -= -struct - module D = Dom (S.D) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include Printable.Option (S.P) (struct let name = "None" end) - - let of_elt = function - | `Lifted x -> Some (S.P.of_elt x) - | _ -> None - end - - let name () = S.name ()^" lifted" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - - let startcontext () = S.startcontext () - let startstate v = `Lifted (S.startstate v) - let exitstate v = `Lifted (S.exitstate v) - let morphstate v d = try `Lifted (S.morphstate v (D.unlift d)) with Deadcode -> d - - - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) - } - - let context ctx fd = S.context (conv ctx) fd % D.unlift - - let lift_fun ctx f g h b = - try f @@ h (g (conv ctx)) - with Deadcode -> b - - let sync ctx reason = lift_fun ctx D.lift S.sync ((|>) reason) `Bot - - let enter ctx r f args = - let liftmap = List.map (fun (x,y) -> D.lift x, D.lift y) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) [] - - let paths_as_set ctx = - let liftmap = List.map (fun x -> D.lift x) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) [D.bot ()] (* One dead path instead of none, such that combine_env gets called for functions with dead normal return (and thus longjmpy returns can be correctly handled by lifter). *) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun (x) -> x q) (Queries.Result.bot q) - let assign ctx lv e = lift_fun ctx D.lift S.assign ((|>) e % (|>) lv) `Bot - let vdecl ctx v = lift_fun ctx D.lift S.vdecl ((|>) v) `Bot - let branch ctx e tv = lift_fun ctx D.lift S.branch ((|>) tv % (|>) e) `Bot - let body ctx f = lift_fun ctx D.lift S.body ((|>) f) `Bot - let return ctx r f = lift_fun ctx D.lift S.return ((|>) f % (|>) r) `Bot - let asm ctx = lift_fun ctx D.lift S.asm identity `Bot - let skip ctx = lift_fun ctx D.lift S.skip identity `Bot - let special ctx r f args = lift_fun ctx D.lift S.special ((|>) args % (|>) f % (|>) r) `Bot - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx D.lift (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot - - let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot -end - -module type Increment = -sig - val increment: increment_data option -end - - -(** The main point of this file---generating a [GlobConstrSys] from a [Spec]. *) -module FromSpec (S:Spec) (Cfg:CfgBackward) (I: Increment) - : sig - include GlobConstrSys with module LVar = VarF (S.C) - and module GVar = GVarF (S.V) - and module D = S.D - and module G = GVarG (S.G) (S.C) - end -= -struct - type lv = MyCFG.node * S.C.t - (* type gv = varinfo *) - type ld = S.D.t - (* type gd = S.G.t *) - module LVar = VarF (S.C) - module GVar = GVarF (S.V) - module D = S.D - module G = GVarG (S.G) (S.C) - - (* Two global invariants: - 1. S.V -> S.G -- used for Spec - 2. fundec -> set of S.C -- used for IterSysVars Node *) - - let sync ctx = - match ctx.prev_node, Cfg.prev ctx.prev_node with - | _, _ :: _ :: _ -> (* Join in CFG. *) - S.sync ctx `Join - | FunctionEntry f, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) - S.sync ctx (`JoinCall f) - | _, _ -> S.sync ctx `Normal - - let side_context sideg f c = - if !AnalysisState.postsolving then - sideg (GVar.contexts f) (G.create_contexts (G.CSet.singleton c)) - - let common_ctx var edge prev_node pval (getl:lv -> ld) sidel getg sideg : (D.t, S.G.t, S.C.t, S.V.t) ctx * D.t list ref * (lval option * varinfo * exp list * D.t * bool) list ref = - let r = ref [] in - let spawns = ref [] in - (* now watch this ... *) - let rec ctx = - { ask = (fun (type a) (q: a Queries.t) -> S.query ctx q) - ; emit = (fun _ -> failwith "emit outside MCP") - ; node = fst var - ; prev_node = prev_node - ; control_context = snd var |> Obj.obj - ; context = snd var |> Obj.obj - ; edge = edge - ; local = pval - ; global = (fun g -> G.spec (getg (GVar.spec g))) - ; spawn = spawn - ; split = (fun (d:D.t) es -> assert (List.is_empty es); r := d::!r) - ; sideg = (fun g d -> sideg (GVar.spec g) (G.create_spec d)) - } - and spawn ?(multiple=false) lval f args = - (* TODO: adjust ctx node/edge? *) - (* TODO: don't repeat for all paths that spawn same *) - let ds = S.threadenter ~multiple ctx lval f args in - List.iter (fun d -> - spawns := (lval, f, args, d, multiple) :: !spawns; - match Cilfacade.find_varinfo_fundec f with - | fd -> - let c = S.context ctx fd d in - sidel (FunctionEntry fd, c) d; - ignore (getl (Function fd, c)) - | exception Not_found -> - (* unknown function *) - M.error ~category:Imprecise ~tags:[Category Unsound] "Created a thread from unknown function %s" f.vname - (* actual implementation (e.g. invalidation) is done by threadenter *) - ) ds - in - (* ... nice, right! *) - let pval = sync ctx in - { ctx with local = pval }, r, spawns - - let rec bigsqcup = function - | [] -> D.bot () - | [x] -> x - | x::xs -> D.join x (bigsqcup xs) - - let thread_spawns ctx d spawns = - if List.is_empty spawns then - d - else - let rec ctx' = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query ctx' q) - ; local = d - } - in - (* TODO: don't forget path dependencies *) - let one_spawn (lval, f, args, fd, multiple) = - let rec fctx = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query fctx q) - ; local = fd - } - in - S.threadspawn ctx' ~multiple lval f args fctx - in - bigsqcup (List.map one_spawn spawns) - - let common_join ctx d splits spawns = - thread_spawns ctx (bigsqcup (d :: splits)) spawns - - let common_joins ctx ds splits spawns = common_join ctx (bigsqcup ds) splits spawns - - let tf_assign var edge prev_node lv e getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.assign ctx lv e in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let tf_vdecl var edge prev_node v getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.vdecl ctx v in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let normal_return r fd ctx sideg = - let spawning_return = S.return ctx r fd in - let nval = S.sync { ctx with local = spawning_return } `Return in - nval - - let toplevel_kernel_return r fd ctx sideg = - let st = if fd.svar.vname = MyCFG.dummy_func.svar.vname then ctx.local else S.return ctx r fd in - let spawning_return = S.return {ctx with local = st} None MyCFG.dummy_func in - let nval = S.sync { ctx with local = spawning_return } `Return in - nval - - let tf_ret var edge prev_node ret fd getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - if (CilType.Fundec.equal fd MyCFG.dummy_func || - List.mem fd.svar.vname (get_string_list "mainfun")) && - get_bool "kernel" - then toplevel_kernel_return ret fd ctx sideg - else normal_return ret fd ctx sideg - in - common_join ctx d !r !spawns - - let tf_entry var edge prev_node fd getl sidel getg sideg d = - (* Side effect function context here instead of at sidel to FunctionEntry, - because otherwise context for main functions (entrystates) will be missing or pruned during postsolving. *) - let c: unit -> S.C.t = snd var |> Obj.obj in - side_context sideg fd (c ()); - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.body ctx fd in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let tf_test var edge prev_node e tv getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.branch ctx e tv in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let tf_normal_call ctx lv e (f:fundec) args getl sidel getg sideg = - let combine (cd, fc, fd) = - if M.tracing then M.traceli "combine" "local: %a" S.D.pretty cd; - if M.tracing then M.trace "combine" "function: %a" S.D.pretty fd; - let rec cd_ctx = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); - local = cd; - } - in - let fd_ctx = - (* Inner scope to prevent unsynced fd_ctx from being used. *) - (* Extra sync in case function has multiple returns. - Each `Return sync is done before joining, so joined value may be unsound. - Since sync is normally done before tf (in common_ctx), simulate it here for fd. *) - (* TODO: don't do this extra sync here *) - let rec sync_ctx = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query sync_ctx q); - local = fd; - prev_node = Function f; - } - in - (* TODO: more accurate ctx? *) - let synced = sync sync_ctx in - let rec fd_ctx = - { sync_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query fd_ctx q); - local = synced; - } - in - fd_ctx - in - let r = List.fold_left (fun acc fd1 -> - let rec fd1_ctx = - { fd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query fd1_ctx q); - local = fd1; - } - in - let combine_enved = S.combine_env cd_ctx lv e f args fc fd1_ctx.local (Analyses.ask_of_ctx fd1_ctx) in - let rec combine_assign_ctx = - { cd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query combine_assign_ctx q); - local = combine_enved; - } - in - S.D.join acc (S.combine_assign combine_assign_ctx lv e f args fc fd1_ctx.local (Analyses.ask_of_ctx fd1_ctx)) - ) (S.D.bot ()) (S.paths_as_set fd_ctx) - in - if M.tracing then M.traceu "combine" "combined local: %a" S.D.pretty r; - r - in - let paths = S.enter ctx lv f args in - let paths = List.map (fun (c,v) -> (c, S.context ctx f v, v)) paths in - List.iter (fun (c,fc,v) -> if not (S.D.is_bot v) then sidel (FunctionEntry f, fc) v) paths; - let paths = List.map (fun (c,fc,v) -> (c, fc, if S.D.is_bot v then v else getl (Function f, fc))) paths in - (* Don't filter bot paths, otherwise LongjmpLifter is not called. *) - (* let paths = List.filter (fun (c,fc,v) -> not (D.is_bot v)) paths in *) - let paths = List.map (Tuple3.map2 Option.some) paths in - if M.tracing then M.traceli "combine" "combining"; - let paths = List.map combine paths in - let r = List.fold_left D.join (D.bot ()) paths in - if M.tracing then M.traceu "combine" "combined: %a" S.D.pretty r; - r - - let tf_special_call ctx lv f args = S.special ctx lv f args - - let tf_proc var edge prev_node lv e args getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let functions = - match e with - | Lval (Var v, NoOffset) -> - (* Handle statically known function call directly. - Allows deactivating base. *) - [v] - | _ -> - (* Depends on base for query. *) - let ad = ctx.ask (Queries.EvalFunvar e) in - Queries.AD.to_var_may ad (* TODO: don't convert, handle UnknownPtr below *) - in - let one_function f = - match f.vtype with - | TFun (_, params, var_arg, _) -> - let arg_length = List.length args in - let p_length = Option.map_default List.length 0 params in - (* Check whether number of arguments fits. *) - (* If params is None, the function or its parameters are not declared, so we still analyze the unknown function call. *) - if Option.is_none params || p_length = arg_length || (var_arg && arg_length >= p_length) then - begin Some (match Cilfacade.find_varinfo_fundec f with - | fd when LibraryFunctions.use_special f.vname -> - M.info ~category:Analyzer "Using special for defined function %s" f.vname; - tf_special_call ctx lv f args - | fd -> - tf_normal_call ctx lv e fd args getl sidel getg sideg - | exception Not_found -> - tf_special_call ctx lv f args) - end - else begin - let geq = if var_arg then ">=" else "" in - M.warn ~category:Unsound ~tags:[Category Call; CWE 685] "Potential call to function %a with wrong number of arguments (expected: %s%d, actual: %d). This call will be ignored." CilType.Varinfo.pretty f geq p_length arg_length; - None - end - | _ -> - M.warn ~category:Call "Something that is not a function (%a) is called." CilType.Varinfo.pretty f; - None - in - let funs = List.filter_map one_function functions in - if [] = funs && not (S.D.is_bot ctx.local) then begin - M.msg_final Warning ~category:Unsound ~tags:[Category Call] "No suitable function to call"; - M.warn ~category:Unsound ~tags:[Category Call] "No suitable function to be called at call site. Continuing with state before call."; - d (* because LevelSliceLifter *) - end else - common_joins ctx funs !r !spawns - - let tf_asm var edge prev_node getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.asm ctx in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let tf_skip var edge prev_node getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.skip ctx in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let tf var getl sidel getg sideg prev_node edge d = - begin match edge with - | Assign (lv,rv) -> tf_assign var edge prev_node lv rv - | VDecl (v) -> tf_vdecl var edge prev_node v - | Proc (r,f,ars) -> tf_proc var edge prev_node r f ars - | Entry f -> tf_entry var edge prev_node f - | Ret (r,fd) -> tf_ret var edge prev_node r fd - | Test (p,b) -> tf_test var edge prev_node p b - | ASM (_, _, _) -> tf_asm var edge prev_node (* TODO: use ASM fields for something? *) - | Skip -> tf_skip var edge prev_node - end getl sidel getg sideg d - - type Goblint_backtrace.mark += TfLocation of location - - let () = Goblint_backtrace.register_mark_printer (function - | TfLocation loc -> - Some ("transfer function at " ^ CilType.Location.show loc) - | _ -> None (* for other marks *) - ) - - let tf var getl sidel getg sideg prev_node (_,edge) d (f,t) = - let old_loc = !Goblint_tracing.current_loc in - let old_loc2 = !Goblint_tracing.next_loc in - Goblint_tracing.current_loc := f; - Goblint_tracing.next_loc := t; - Goblint_backtrace.protect ~mark:(fun () -> TfLocation f) ~finally:(fun () -> - Goblint_tracing.current_loc := old_loc; - Goblint_tracing.next_loc := old_loc2 - ) (fun () -> - let d = tf var getl sidel getg sideg prev_node edge d in - d - ) - - let tf (v,c) (edges, u) getl sidel getg sideg = - let pval = getl (u,c) in - let _, locs = List.fold_right (fun (f,e) (t,xs) -> f, (f,t)::xs) edges (Node.location v,[]) in - List.fold_left2 (|>) pval (List.map (tf (v,Obj.repr (fun () -> c)) getl sidel getg sideg u) edges) locs - - let tf (v,c) (e,u) getl sidel getg sideg = - let old_node = !current_node in - let old_fd = Option.map Node.find_fundec old_node |? Cil.dummyFunDec in - let new_fd = Node.find_fundec v in - if not (CilType.Fundec.equal old_fd new_fd) then - Timing.Program.enter new_fd.svar.vname; - let old_context = !M.current_context in - current_node := Some u; - M.current_context := Some (Obj.magic c); (* magic is fine because Spec is top-level Control Spec *) - Fun.protect ~finally:(fun () -> - current_node := old_node; - M.current_context := old_context; - if not (CilType.Fundec.equal old_fd new_fd) then - Timing.Program.exit new_fd.svar.vname - ) (fun () -> - let d = tf (v,c) (e,u) getl sidel getg sideg in - d - ) - - let system (v,c) = - match v with - | FunctionEntry _ -> - None - | _ -> - let tf getl sidel getg sideg = - let tf' eu = tf (v,c) eu getl sidel getg sideg in - - match NodeH.find_option CfgTools.node_scc_global v with - | Some scc when NodeH.mem scc.prev v && NodeH.length scc.prev = 1 -> - (* Limited to loops with only one entry node. Otherwise unsound as is. *) - (* TODO: Is it possible to do soundly for multi-entry loops? *) - let stricts = NodeH.find_default scc.prev v [] in - let xs_stricts = List.map tf' stricts in - (* Evaluate non-strict for dead code warnings. See 00-sanity/36-strict-loop-dead. *) - let equal = [%eq: (CilType.Location.t * Edge.t) list * Node.t] in - let is_strict eu = List.exists (equal eu) stricts in - let non_stricts = List.filter (neg is_strict) (Cfg.prev v) in - let xs_non_stricts = List.map tf' non_stricts in - if List.for_all S.D.is_bot xs_stricts then - S.D.bot () - else ( - let xs_strict = List.fold_left S.D.join (S.D.bot ()) xs_stricts in - List.fold_left S.D.join xs_strict xs_non_stricts - ) - | _ -> - let xs = List.map tf' (Cfg.prev v) in - List.fold_left S.D.join (S.D.bot ()) xs - in - Some tf - - let iter_vars getl getg vq fl fg = - (* vars for Spec *) - let rec ctx = - { ask = (fun (type a) (q: a Queries.t) -> S.query ctx q) - ; emit = (fun _ -> failwith "Cannot \"emit\" in query context.") - ; node = MyCFG.dummy_node (* TODO maybe ask should take a node (which could be used here) instead of a location *) - ; prev_node = MyCFG.dummy_node - ; control_context = (fun () -> ctx_failwith "No context in query context.") - ; context = (fun () -> ctx_failwith "No context in query context.") - ; edge = MyCFG.Skip - ; local = S.startstate Cil.dummyFunDec.svar (* bot and top both silently raise and catch Deadcode in DeadcodeLifter *) - ; global = (fun g -> G.spec (getg (GVar.spec g))) - ; spawn = (fun ?(multiple=false) v d -> failwith "Cannot \"spawn\" in query context.") - ; split = (fun d es -> failwith "Cannot \"split\" in query context.") - ; sideg = (fun v g -> failwith "Cannot \"split\" in query context.") - } - in - let f v = fg (GVar.spec (Obj.obj v)) in - S.query ctx (IterSysVars (vq, f)); - - (* node vars for locals *) - match vq with - | Node {node; fundec} -> - let fd = Option.default_delayed (fun () -> Node.find_fundec node) fundec in - let cs = G.contexts (getg (GVar.contexts fd)) in - G.CSet.iter (fun c -> - fl (node, c) - ) cs - | _ -> - () - - let sys_change getl getg = - let open CompareCIL in - - let c = match I.increment with - | Some {changes; _} -> changes - | None -> empty_change_info () - in - List.(Logs.info "change_info = { unchanged = %d; changed = %d (with unchangedHeader = %d); added = %d; removed = %d }" (length c.unchanged) (length c.changed) (BatList.count_matching (fun c -> c.unchangedHeader) c.changed) (length c.added) (length c.removed)); - - let changed_funs = List.filter_map (function - | {old = {def = Some (Fun f); _}; diff = None; _} -> - Logs.info "Completely changed function: %s" f.svar.vname; - Some f - | _ -> None - ) c.changed - in - let part_changed_funs = List.filter_map (function - | {old = {def = Some (Fun f); _}; diff = Some nd; _} -> - Logs.info "Partially changed function: %s" f.svar.vname; - Some (f, nd.primObsoleteNodes, nd.unchangedNodes) - | _ -> None - ) c.changed - in - let removed_funs = List.filter_map (function - | {def = Some (Fun f); _} -> - Logs.info "Removed function: %s" f.svar.vname; - Some f - | _ -> None - ) c.removed - in - - let module HM = Hashtbl.Make (Var2 (LVar) (GVar)) in - - let mark_node hm f node = - iter_vars getl getg (Node {node; fundec = Some f}) (fun v -> - HM.replace hm (`L v) () - ) (fun v -> - HM.replace hm (`G v) () - ) - in - - let reluctant = GobConfig.get_bool "incremental.reluctant.enabled" in - let reanalyze_entry f = - (* destabilize the entry points of a changed function when reluctant is off, - or the function is to be force-reanalyzed *) - (not reluctant) || CompareCIL.VarinfoSet.mem f.svar c.exclude_from_rel_destab - in - let obsolete_ret = HM.create 103 in - let obsolete_entry = HM.create 103 in - let obsolete_prim = HM.create 103 in - - (* When reluctant is on: - Only add function entry nodes to obsolete_entry if they are in force-reanalyze *) - List.iter (fun f -> - if reanalyze_entry f then - (* collect function entry for eager destabilization *) - mark_node obsolete_entry f (FunctionEntry f) - else - (* collect function return for reluctant analysis *) - mark_node obsolete_ret f (Function f) - ) changed_funs; - (* Primary changed unknowns from partially changed functions need only to be collected for eager destabilization when reluctant is off *) - (* The return nodes of partially changed functions are collected in obsolete_ret for reluctant analysis *) - (* We utilize that force-reanalyzed functions are always considered as completely changed (and not partially changed) *) - List.iter (fun (f, pn, _) -> - if not reluctant then ( - List.iter (fun n -> - mark_node obsolete_prim f n - ) pn - ) - else - mark_node obsolete_ret f (Function f) - ) part_changed_funs; - - let obsolete = Enum.append (HM.keys obsolete_entry) (HM.keys obsolete_prim) |> List.of_enum in - let reluctant = HM.keys obsolete_ret |> List.of_enum in - - let marked_for_deletion = HM.create 103 in - - let dummy_pseudo_return_node f = - (* not the same as in CFG, but compares equal because of sid *) - Node.Statement ({Cil.dummyStmt with sid = Cilfacade.get_pseudo_return_id f}) - in - let add_nodes_of_fun (functions: fundec list) (withEntry: fundec -> bool) = - let add_stmts (f: fundec) = - List.iter (fun s -> - mark_node marked_for_deletion f (Statement s) - ) f.sallstmts - in - List.iter (fun f -> - if withEntry f then - mark_node marked_for_deletion f (FunctionEntry f); - mark_node marked_for_deletion f (Function f); - add_stmts f; - mark_node marked_for_deletion f (dummy_pseudo_return_node f) - ) functions; - in - - add_nodes_of_fun changed_funs reanalyze_entry; - add_nodes_of_fun removed_funs (fun _ -> true); - (* it is necessary to remove all unknowns for changed pseudo-returns because they have static ids *) - let add_pseudo_return f un = - let pseudo = dummy_pseudo_return_node f in - if not (List.exists (Node.equal pseudo % fst) un) then - mark_node marked_for_deletion f (dummy_pseudo_return_node f) - in - List.iter (fun (f,_,un) -> - mark_node marked_for_deletion f (Function f); - add_pseudo_return f un - ) part_changed_funs; - - let delete = HM.keys marked_for_deletion |> List.of_enum in - - let restart = match I.increment with - | Some data -> - let restart = ref [] in - List.iter (fun g -> - iter_vars getl getg g (fun v -> - restart := `L v :: !restart - ) (fun v -> - restart := `G v :: !restart - ) - ) data.restarting; - !restart - | None -> [] - in - - {obsolete; delete; reluctant; restart} -end - - -(** Add path sensitivity to a analysis *) -module PathSensitive2 (Spec:Spec) - : Spec - with module G = Spec.G - and module C = Spec.C - and module V = Spec.V -= -struct - module D = - struct - (* TODO is it really worth it to check every time instead of just using sets and joining later? *) - module R = - struct - include Spec.P - type elt = Spec.D.t - end - module J = SetDomain.Joined (Spec.D) - include DisjointDomain.ProjectiveSet (Spec.D) (J) (R) - let name () = "PathSensitive (" ^ name () ^ ")" - - let printXml f x = - let print_one x = - BatPrintf.fprintf f "\n%a" Spec.D.printXml x - in - iter print_one x - end - - module G = Spec.G - module C = Spec.C - module V = Spec.V - module P = UnitP - - let name () = "PathSensitive2("^Spec.name ()^")" - - type marshal = Spec.marshal - let init = Spec.init - let finalize = Spec.finalize - - let startcontext () = Spec.startcontext () - let exitstate v = D.singleton (Spec.exitstate v) - let startstate v = D.singleton (Spec.startstate v) - let morphstate v d = D.map (Spec.morphstate v) d - - let conv ctx x = - let rec ctx' = { ctx with ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx' q) - ; local = x - ; split = (ctx.split % D.singleton) } - in - ctx' - - let context ctx fd l = - if D.cardinal l <> 1 then - failwith "PathSensitive2.context must be called with a singleton set." - else - let x = D.choose l in - Spec.context (conv ctx x) fd x - - - let map ctx f g = - let h x xs = - try D.add (g (f (conv ctx x))) xs - with Deadcode -> xs - in - let d = D.fold h ctx.local (D.empty ()) in - if D.is_bot d then raise Deadcode else d - - let fold' ctx f g h a = - let k x a = - try h a @@ g @@ f @@ conv ctx x - with Deadcode -> a - in - D.fold k ctx.local a - - let assign ctx l e = map ctx Spec.assign (fun h -> h l e ) - let vdecl ctx v = map ctx Spec.vdecl (fun h -> h v) - let body ctx f = map ctx Spec.body (fun h -> h f ) - let return ctx e f = map ctx Spec.return (fun h -> h e f ) - let branch ctx e tv = map ctx Spec.branch (fun h -> h e tv) - let asm ctx = map ctx Spec.asm identity - let skip ctx = map ctx Spec.skip identity - let special ctx l f a = map ctx Spec.special (fun h -> h l f a) - - let event ctx e octx = - let fd1 = D.choose octx.local in - map ctx Spec.event (fun h -> h e (conv octx fd1)) - - let threadenter ctx ~multiple lval f args = - let g xs ys = (List.map (fun y -> D.singleton y) ys) @ xs in - fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] - - let threadspawn ctx ~multiple lval f args fctx = - let fd1 = D.choose fctx.local in - map ctx (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fctx fd1)) - - let sync ctx reason = map ctx Spec.sync (fun h -> h reason) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - (* TODO: handle Invariant path like PathSensitive3? *) - (* join results so that they are sound for all paths *) - let module Result = (val Queries.Result.lattice q) in - fold' ctx Spec.query identity (fun x f -> Result.join x (f q)) (Result.bot ()) - - let enter ctx l f a = - let g xs ys = (List.map (fun (x,y) -> D.singleton x, D.singleton y) ys) @ xs in - fold' ctx Spec.enter (fun h -> h l f a) g [] - - let paths_as_set ctx = - (* Path-sensitivity is only here, not below! *) - let elems = D.elements ctx.local in - List.map (D.singleton) elems - - let combine_env ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in - let k x y = - if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; - try - let r = Spec.combine_env (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; - D.add r y - with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead"; - y - in - let d = D.fold k d (D.bot ()) in - if D.is_bot d then raise Deadcode else d - - let combine_assign ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in - let k x y = - if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; - try - let r = Spec.combine_assign (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; - D.add r y - with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead"; - y - in - let d = D.fold k d (D.bot ()) in - if D.is_bot d then raise Deadcode else d -end - -module DeadBranchLifter (S: Spec): Spec = -struct - include S - - let name () = "DeadBranch (" ^ S.name () ^ ")" - - (* Two global invariants: - 1. S.V -> S.G -- used for S - 2. node -> (exp -> flat bool) -- used for warnings *) - - module V = - struct - include Printable.EitherConf (struct let expand1 = false let expand2 = true end) (S.V) (Node) - let name () = "DeadBranch" - let s x = `Left x - let node x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | `Right _ -> true - end - - module EM = - struct - include MapDomain.MapBot (Basetype.CilExp) (BoolDomain.FlatBool) - let name () = "branches" - end - - module G = - struct - include Lattice.Lift2 (S.G) (EM) - let name () = "deadbranch" - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "DeadBranchLifter.s" - let node = function - | `Bot -> EM.bot () - | `Lifted2 x -> x - | _ -> failwith "DeadBranchLifter.node" - let create_s s = `Lifted1 s - let create_node node = `Lifted2 node - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> - let em = G.node (ctx.global (V.node g)) in - EM.iter (fun exp tv -> - match tv with - | `Lifted tv -> - let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) - let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in - M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv - | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) - M.msg_final Error ~category:Analyzer ~tags:[Category Unsound] "Both branches dead"; - M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp - | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) - | `Top -> (* may be both true and false *) - () - ) em; - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - - (* node vars for dead branches *) - begin match vq with - | Node {node; _} -> - vf (Obj.repr (V.node node)) - | _ -> - () - end - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - let context ctx = S.context (conv ctx) - - let branch ctx exp tv = - if !AnalysisState.postsolving then ( - try - let r = branch ctx exp tv in - (* branch is live *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) - r - with Deadcode -> - (* branch is dead *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) - raise Deadcode - ) - else ( - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) - branch ctx exp tv - ) - - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - -module LongjmpLifter (S: Spec): Spec = -struct - include S - - let name () = "Longjmp (" ^ S.name () ^ ")" - - module V = - struct - include Printable.Either3Conf (struct let expand1 = false let expand2 = true let expand3 = true end) (S.V) (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C)) - let name () = "longjmp" - let s x = `Left x - let longjmpto x = `Middle x - let longjmpret x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | _ -> false - end - - module G = - struct - include Lattice.Lift2 (S.G) (S.D) - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "LongjmpLifter.s" - let local = function - | `Bot -> S.D.bot () - | `Lifted2 x -> x - | _ -> failwith "LongjmpLifter.local" - let create_s s = `Lifted1 s - let create_local local = `Lifted2 local - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" S.D.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | _ -> - Queries.Result.top q - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | _ -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - (* TODO: vars? *) - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let context ctx = S.context (conv ctx) - - let combine_env ctx lv e f args fc fd f_ask = - let conv_ctx = conv ctx in - let current_fundec = Node.find_fundec ctx.node in - let handle_longjmp (cd, fc, longfd) = - (* This is called per-path. *) - let rec cd_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); - local = cd; - } - in - let longfd_ctx = - (* Inner scope to prevent unsynced longfd_ctx from being used. *) - (* Extra sync like with normal combine. *) - let rec sync_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query sync_ctx q); - local = longfd; - prev_node = Function f; - } - in - let synced = S.sync sync_ctx `Join in - let rec longfd_ctx = - { sync_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query longfd_ctx q); - local = synced; - } - in - longfd_ctx - in - let combined = lazy ( (* does not depend on target, do at most once *) - (* Globals are non-problematic here, as they are always carried around without any issues! *) - (* A combine call is mostly needed to ensure locals have appropriate values. *) - (* Using f from called function on purpose here! Needed? *) - S.combine_env cd_ctx None e f args fc longfd_ctx.local (Analyses.ask_of_ctx longfd_ctx) (* no lval because longjmp return skips return value assignment *) - ) - in - let returned = lazy ( (* does not depend on target, do at most once *) - let rec combined_ctx = - { cd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query combined_ctx q); - local = Lazy.force combined; - } - in - S.return combined_ctx None current_fundec - ) - in - let (active_targets, _) = longfd_ctx.ask ActiveJumpBuf in - let valid_targets = cd_ctx.ask ValidLongJmp in - let handle_target target = match target with - | JmpBufDomain.BufferEntryOrTop.AllTargets -> () (* The warning is already emitted at the point where the longjmp happens *) - | Target (target_node, target_context) -> - let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Fun: Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force combined)) - (* No need to propagate this outwards here, the set of valid longjumps is part of the context, we can never have the same context setting the longjmp multiple times *) - ) - (* Appropriate setjmp is not in current function & current context *) - else if JmpBufDomain.JmpBufSet.mem target valid_targets then - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) - else - (* It actually is not handled here but was propagated here spuriously, we already warned at the location where this issue is caused *) - (* As the validlongjumps inside the callee is a a superset of the ones inside the caller *) - () - in - JmpBufDomain.JmpBufSet.iter handle_target active_targets - in - if M.tracing then M.tracel "longjmp" "longfd getg %a" CilType.Fundec.pretty f; - let longfd = G.local (ctx.global (V.longjmpret (f, Option.get fc))) in - if M.tracing then M.tracel "longjmp" "longfd %a" D.pretty longfd; - if not (D.is_bot longfd) then - handle_longjmp (ctx.local, fc, longfd); - S.combine_env (conv_ctx) lv e f args fc fd f_ask - - let combine_assign ctx lv e f args fc fd f_ask = - S.combine_assign (conv ctx) lv e f args fc fd f_ask - - let special ctx lv f args = - let conv_ctx = conv ctx in - match (LibraryFunctions.find f).special args with - | Setjmp {env} -> - (* Handling of returning for the first time *) - let normal_return = S.special conv_ctx lv f args in - let jmp_return = G.local (ctx.global (V.longjmpto (ctx.prev_node, ctx.context ()))) in - if S.D.is_bot jmp_return then - normal_return - else ( - let rec jmp_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query jmp_ctx q); - local = jmp_return; - } - in - let longjmped = S.event jmp_ctx (Events.Longjmped {lval=lv}) jmp_ctx in - S.D.join normal_return longjmped - ) - | Longjmp {env; value} -> - let current_fundec = Node.find_fundec ctx.node in - let handle_path path = ( - let rec path_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query path_ctx q); - local = path; - } - in - let specialed = lazy ( (* does not depend on target, do at most once *) - S.special path_ctx lv f args - ) - in - let returned = lazy ( (* does not depend on target, do at most once *) - let rec specialed_ctx = - { path_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query specialed_ctx q); - local = Lazy.force specialed; - } - in - S.return specialed_ctx None current_fundec - ) - in - (* Eval `env` again to avoid having to construct bespoke ctx to ask *) - let targets = path_ctx.ask (EvalJumpBuf env) in - let valid_targets = path_ctx.ask ValidLongJmp in - if M.tracing then Messages.tracel "longjmp" "Jumping to %a" JmpBufDomain.JmpBufSet.pretty targets; - let handle_target target = match target with - | JmpBufDomain.BufferEntryOrTop.AllTargets -> - M.warn ~category:Imprecise "Longjmp to potentially invalid target, as contents of buffer %a may be unknown! (imprecision due to heap?)" d_exp env; - M.msg_final Error ~category:Unsound ~tags:[Category Imprecise; Category Call] "Longjmp to unknown target ignored" - | Target (target_node, target_context) -> - let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force specialed)) - ) - else if JmpBufDomain.JmpBufSet.mem target valid_targets then ( - if M.tracing then Messages.tracel "longjmp" "Longjmp to somewhere else, side-effect to %i" (S.C.hash (ctx.context ())); - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) - ) - else - M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target! (Target %a in Function %a which may have already returned or is in a different thread)" Node.pretty target_node CilType.Fundec.pretty target_fundec - in - if JmpBufDomain.JmpBufSet.is_empty targets then - M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target (%a is bot?!)" d_exp env - else - JmpBufDomain.JmpBufSet.iter handle_target targets - ) - in - List.iter handle_path (S.paths_as_set conv_ctx); - if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( - AnalysisState.svcomp_may_not_terminate := true; - M.warn ~category:Termination "The program might not terminate! (Longjmp)" - ); - S.D.bot () - | _ -> S.special conv_ctx lv f args - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - - -(** Add cycle detection in the context-sensitive dynamic function call graph to an analysis *) -module RecursionTermLifter (S: Spec) - : Spec with module D = S.D - and module C = S.C -= -(* two global invariants: - - S.V -> S.G - Needed to store the previously built global invariants - - fundec * S.C -> (Set (fundec * S.C)) - The second global invariant maps from the callee fundec and context to a set of caller fundecs and contexts. - This structure therefore stores the context-sensitive call graph. - For example: - let the function f in context c call function g in context c'. - In the global invariant structure it would be stored like this: (g,c') -> {(f, c)} -*) - -struct - include S - - (* contains all the callee fundecs and contexts *) - module V = GVarFC(S.V)(S.C) - - (* Tuple containing the fundec and context of a caller *) - module Call = Printable.Prod (CilType.Fundec) (S.C) - - (* Set containing multiple caller tuples *) - module CallerSet = SetDomain.Make (Call) - - module G = - struct - include Lattice.Lift2 (G) (CallerSet) - - let spec = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "RecursionTermLifter.spec" - - let callers = function - | `Bot -> CallerSet.bot () - | `Lifted2 x -> x - | _ -> failwith "RecursionTermLifter.callGraph" - - let create_spec spec = `Lifted1 spec - let create_singleton_caller caller = `Lifted2 (CallerSet.singleton caller) - - let printXml f = function - | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CallerSet.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - - end - - let name () = "RecursionTermLifter (" ^ S.name () ^ ")" - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.spec (ctx.global (V.spec v))); - sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_spec g)); - } - - let cycleDetection ctx call = - let module LH = Hashtbl.Make (Printable.Prod (CilType.Fundec) (S.C)) in - let module LS = Set.Make (Printable.Prod (CilType.Fundec) (S.C)) in - (* find all cycles/SCCs *) - let global_visited_calls = LH.create 100 in - - (* DFS *) - let rec iter_call (path_visited_calls: LS.t) ((fundec, _) as call) = - if LS.mem call path_visited_calls then ( - AnalysisState.svcomp_may_not_terminate := true; (*set the indicator for a non-terminating program for the sv comp*) - (*Cycle found*) - let loc = M.Location.CilLocation fundec.svar.vdecl in - M.warn ~loc ~category:Termination "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec) (* output a warning for non-termination*) - else if not (LH.mem global_visited_calls call) then begin - LH.replace global_visited_calls call (); - let new_path_visited_calls = LS.add call path_visited_calls in - let gvar = V.call call in - let callers = G.callers (ctx.global gvar) in - CallerSet.iter (fun to_call -> - iter_call new_path_visited_calls to_call - ) callers; - end - in - iter_call LS.empty call - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal v -> - (* check result of loop analysis *) - if not (ctx.ask Queries.MustTermAllLoops) then - AnalysisState.svcomp_may_not_terminate := true; - let v: V.t = Obj.obj v in - begin match v with - | `Left v' -> - S.query (conv ctx) (WarnGlobal (Obj.repr v')) - | `Right call -> cycleDetection ctx call (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) - end - | InvariantGlobal v -> - let v: V.t = Obj.obj v in - begin match v with - | `Left v -> - S.query (conv ctx) (InvariantGlobal (Obj.repr v)) - | `Right v -> - Queries.Result.top q - end - | _ -> S.query (conv ctx) q - - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - - - let record_call sideg callee caller = - sideg (V.call callee) (G.create_singleton_caller caller) - - let enter ctx = S.enter (conv ctx) - let context ctx = S.context (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx r fe f args fc es f_ask = - if !AnalysisState.postsolving then ( - let c_r: S.C.t = ctx.context () in (* Caller context *) - let nodeF = ctx.node in - let fd_r : fundec = Node.find_fundec nodeF in (* Caller fundec *) - let caller: (fundec * S.C.t) = (fd_r, c_r) in - let c_e: S.C.t = Option.get fc in (* Callee context *) - let fd_e : fundec = f in (* Callee fundec *) - let callee = (fd_e, c_e) in - record_call ctx.sideg callee caller - ); - S.combine_env (conv ctx) r fe f args fc es f_ask - - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end module CompareGlobSys (SpecSys: SpecSys) = struct From 0061608f3b9bd0c13e4d7fd81819262797124314 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 14:57:50 +0300 Subject: [PATCH 374/566] Remove compare modules from Constraints --- src/framework/constraints.ml | 212 ----------------------------------- 1 file changed, 212 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index 23cc297439..a8728f2548 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -1708,215 +1708,3 @@ struct let asm ctx = S.asm (conv ctx) let event ctx e octx = S.event (conv ctx) e (conv octx) end - -module CompareGlobSys (SpecSys: SpecSys) = -struct - open SpecSys - module Sys = EQSys - module LH = LHT - module GH = GHT - - open Spec - module G = Sys.G - - module PP = Hashtbl.Make (Node) - - let compare_globals g1 g2 = - let eq, le, gr, uk = ref 0, ref 0, ref 0, ref 0 in - let f_eq () = incr eq in - let f_le () = incr le in - let f_gr () = incr gr in - let f_uk () = incr uk in - let f k v1 = - let v2 = try GH.find g2 k with Not_found -> G.bot () in - let b1 = G.leq v1 v2 in - let b2 = G.leq v2 v1 in - if b1 && b2 then - f_eq () - else if b1 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "Global %a is more precise using left:\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v2,v1); - f_le () - end else if b2 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "Global %a is more precise using right:\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v1,v2); - f_gr () - end else begin - if get_bool "dbg.compare_runs.diff" then ( - Logs.info "Global %a is incomparable (diff):\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v1,v2); - Logs.info "Global %a is incomparable (reverse diff):\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v2,v1); - ); - f_uk () - end - in - GH.iter f g1; - Logs.info "globals:\tequal = %d\tleft = %d\tright = %d\tincomparable = %d" !eq !le !gr !uk - - let compare_locals h1 h2 = - let eq, le, gr, uk = ref 0, ref 0, ref 0, ref 0 in - let f k v1 = - if PP.mem h2 k then - let v2 = PP.find h2 k in - let b1 = D.leq v1 v2 in - let b2 = D.leq v2 v1 in - if b1 && b2 then - incr eq - else if b1 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "%a @@ %a is more precise using left:\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v2,v1); - incr le - end else if b2 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "%a @@ %a is more precise using right:\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v1,v2); - incr gr - end else begin - if get_bool "dbg.compare_runs.diff" then ( - Logs.info "%a @@ %a is incomparable (diff):\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v1,v2); - Logs.info "%a @@ %a is incomparable (reverse diff):\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v2,v1); - ); - incr uk - end - in - PP.iter f h1; - (* let k1 = Set.of_enum @@ PP.keys h1 in - let k2 = Set.of_enum @@ PP.keys h2 in - let o1 = Set.cardinal @@ Set.diff k1 k2 in - let o2 = Set.cardinal @@ Set.diff k2 k1 in - Logs.info "locals: \tequal = %d\tleft = %d[%d]\tright = %d[%d]\tincomparable = %d" !eq !le o1 !gr o2 !uk *) - Logs.info "locals: \tequal = %d\tleft = %d\tright = %d\tincomparable = %d" !eq !le !gr !uk - - let compare_locals_ctx h1 h2 = - let eq, le, gr, uk, no2, no1 = ref 0, ref 0, ref 0, ref 0, ref 0, ref 0 in - let f_eq () = incr eq in - let f_le () = incr le in - let f_gr () = incr gr in - let f_uk () = incr uk in - let f k v1 = - if not (LH.mem h2 k) then incr no2 else - let v2 = LH.find h2 k in - let b1 = D.leq v1 v2 in - let b2 = D.leq v2 v1 in - if b1 && b2 then - f_eq () - else if b1 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "%a is more precise using left:\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v2,v1); - f_le () - end else if b2 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "%a is more precise using right:\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v1,v2); - f_gr () - end else begin - if get_bool "dbg.compare_runs.diff" then ( - Logs.info "%a is incomparable (diff):\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v1,v2); - Logs.info "%a is incomparable (reverse diff):\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v2,v1); - ); - f_uk () - end - in - LH.iter f h1; - let f k v2 = - if not (LH.mem h1 k) then incr no1 - in - LH.iter f h2; - (* let k1 = Set.of_enum @@ PP.keys h1 in *) - (* let k2 = Set.of_enum @@ PP.keys h2 in *) - (* let o1 = Set.cardinal @@ Set.diff k1 k2 in *) - (* let o2 = Set.cardinal @@ Set.diff k2 k1 in *) - Logs.info "locals_ctx:\tequal = %d\tleft = %d\tright = %d\tincomparable = %d\tno_ctx_in_right = %d\tno_ctx_in_left = %d" !eq !le !gr !uk !no2 !no1 - - let compare (name1,name2) (l1,g1) (l2,g2) = - let one_ctx (n,_) v h = - PP.replace h n (try D.join v (PP.find h n) with Not_found -> v); - h - in - (* these contain results where the contexts per node have been joined *) - let h1 = PP.create 113 in - let h2 = PP.create 113 in - let _ = LH.fold one_ctx l1 h1 in - let _ = LH.fold one_ctx l2 h2 in - Logs.newline (); - Logs.info "Comparing GlobConstrSys precision of %s (left) with %s (right):" name1 name2; - compare_globals g1 g2; - compare_locals h1 h2; - compare_locals_ctx l1 l2; - Logs.newline (); -end - -module CompareHashtbl (Var: VarType) (Dom: Lattice.S) (VH: Hashtbl.S with type key = Var.t) = -struct - module Var = - struct - include Printable.Std - include Var - let name () = "var" - - let pretty = pretty_trace - include Printable.SimplePretty ( - struct - type nonrec t = t - let pretty = pretty - end - ) - end - - include PrecCompare.MakeHashtbl (Var) (Dom) (VH) -end - -module CompareEqSys (Sys: EqConstrSys) (VH: Hashtbl.S with type key = Sys.Var.t) = -struct - module Compare = CompareHashtbl (Sys.Var) (Sys.Dom) (VH) - - let compare (name1, name2) vh1 vh2 = - Logs.newline (); - Logs.info "Comparing EqConstrSys precision of %s (left) with %s (right):" name1 name2; - let verbose = get_bool "dbg.compare_runs.diff" in - let (_, msg) = Compare.compare ~verbose ~name1 vh1 ~name2 vh2 in - Logs.info "EqConstrSys comparison summary: %t" (fun () -> msg); - Logs.newline (); -end - -module CompareGlobal (GVar: VarType) (G: Lattice.S) (GH: Hashtbl.S with type key = GVar.t) = -struct - module Compare = CompareHashtbl (GVar) (G) (GH) - - let compare (name1, name2) vh1 vh2 = - Logs.newline (); - Logs.info "Comparing globals precision of %s (left) with %s (right):" name1 name2; - let verbose = get_bool "dbg.compare_runs.diff" in - let (_, msg) = Compare.compare ~verbose ~name1 vh1 ~name2 vh2 in - Logs.info "Globals comparison summary: %t" (fun () -> msg); - Logs.newline (); -end - -module CompareNode (C: Printable.S) (D: Lattice.S) (LH: Hashtbl.S with type key = VarF (C).t) = -struct - module Node = - struct - include Node - let var_id _ = "nodes" - let node x = x - let is_write_only _ = false - end - module NH = Hashtbl.Make (Node) - - module Compare = CompareHashtbl (Node) (D) (NH) - - let join_contexts (lh: D.t LH.t): D.t NH.t = - let nh = NH.create 113 in - LH.iter (fun (n, _) d -> - let d' = try D.join (NH.find nh n) d with Not_found -> d in - NH.replace nh n d' - ) lh; - nh - - let compare (name1, name2) vh1 vh2 = - Logs.newline (); - Logs.info "Comparing nodes precision of %s (left) with %s (right):" name1 name2; - let vh1' = join_contexts vh1 in - let vh2' = join_contexts vh2 in - let verbose = get_bool "dbg.compare_runs.diff" in - let (_, msg) = Compare.compare ~verbose ~name1 vh1' ~name2 vh2' in - Logs.info "Nodes comparison summary: %t" (fun () -> msg); - Logs.newline (); -end From 5a3d53d44b7945d0b7056a0f2ced1899dbcbdb8e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:02:20 +0300 Subject: [PATCH 375/566] Rename Constraints -> SpecLifters for split --- src/framework/control.ml | 2 +- src/framework/{constraints.ml => specLifters.ml} | 0 src/goblint_lib.ml | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/framework/{constraints.ml => specLifters.ml} (100%) diff --git a/src/framework/control.ml b/src/framework/control.ml index ada639bb59..b156acc1d3 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -8,7 +8,7 @@ open MyCFG open Analyses open ConstrSys open GobConfig -open Constraints +open SpecLifters module type S2S = Spec2Spec diff --git a/src/framework/constraints.ml b/src/framework/specLifters.ml similarity index 100% rename from src/framework/constraints.ml rename to src/framework/specLifters.ml diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index e1b04afa2e..fa610643ad 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -22,7 +22,7 @@ module CfgTools = CfgTools module Analyses = Analyses module ConstrSys = ConstrSys -module Constraints = Constraints +module SpecLifters = SpecLifters module CompareConstraints = CompareConstraints module AnalysisState = AnalysisState module AnalysisStateUtil = AnalysisStateUtil From 6b6bb6251b8d271d8869ba820d25870b1c0d8917 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:03:19 +0300 Subject: [PATCH 376/566] Remove FromSpec from SpecLifters --- src/framework/specLifters.ml | 539 ----------------------------------- 1 file changed, 539 deletions(-) diff --git a/src/framework/specLifters.ml b/src/framework/specLifters.ml index a8728f2548..2012b5cdcf 100644 --- a/src/framework/specLifters.ml +++ b/src/framework/specLifters.ml @@ -1,15 +1,8 @@ -(** Construction of a {{!Analyses.MonSystem} constraint system} from an {{!Analyses.Spec} analysis specification} and {{!MyCFG.CfgBackward} CFGs}. - Transformatons of analysis specifications as functors. *) - open Batteries open GoblintCil -open MyCFG open Analyses -open ConstrSys open GobConfig -module M = Messages - (** Lifts a [Spec] so that the domain is [Hashcons]d *) module HashconsLifter (S:Spec) @@ -506,538 +499,6 @@ struct let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot end -module type Increment = -sig - val increment: increment_data option -end - - -(** The main point of this file---generating a [GlobConstrSys] from a [Spec]. *) -module FromSpec (S:Spec) (Cfg:CfgBackward) (I: Increment) - : sig - include GlobConstrSys with module LVar = VarF (S.C) - and module GVar = GVarF (S.V) - and module D = S.D - and module G = GVarG (S.G) (S.C) - end -= -struct - type lv = MyCFG.node * S.C.t - (* type gv = varinfo *) - type ld = S.D.t - (* type gd = S.G.t *) - module LVar = VarF (S.C) - module GVar = GVarF (S.V) - module D = S.D - module G = GVarG (S.G) (S.C) - - (* Two global invariants: - 1. S.V -> S.G -- used for Spec - 2. fundec -> set of S.C -- used for IterSysVars Node *) - - let sync ctx = - match ctx.prev_node, Cfg.prev ctx.prev_node with - | _, _ :: _ :: _ -> (* Join in CFG. *) - S.sync ctx `Join - | FunctionEntry f, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) - S.sync ctx (`JoinCall f) - | _, _ -> S.sync ctx `Normal - - let side_context sideg f c = - if !AnalysisState.postsolving then - sideg (GVar.contexts f) (G.create_contexts (G.CSet.singleton c)) - - let common_ctx var edge prev_node pval (getl:lv -> ld) sidel getg sideg : (D.t, S.G.t, S.C.t, S.V.t) ctx * D.t list ref * (lval option * varinfo * exp list * D.t * bool) list ref = - let r = ref [] in - let spawns = ref [] in - (* now watch this ... *) - let rec ctx = - { ask = (fun (type a) (q: a Queries.t) -> S.query ctx q) - ; emit = (fun _ -> failwith "emit outside MCP") - ; node = fst var - ; prev_node = prev_node - ; control_context = snd var |> Obj.obj - ; context = snd var |> Obj.obj - ; edge = edge - ; local = pval - ; global = (fun g -> G.spec (getg (GVar.spec g))) - ; spawn = spawn - ; split = (fun (d:D.t) es -> assert (List.is_empty es); r := d::!r) - ; sideg = (fun g d -> sideg (GVar.spec g) (G.create_spec d)) - } - and spawn ?(multiple=false) lval f args = - (* TODO: adjust ctx node/edge? *) - (* TODO: don't repeat for all paths that spawn same *) - let ds = S.threadenter ~multiple ctx lval f args in - List.iter (fun d -> - spawns := (lval, f, args, d, multiple) :: !spawns; - match Cilfacade.find_varinfo_fundec f with - | fd -> - let c = S.context ctx fd d in - sidel (FunctionEntry fd, c) d; - ignore (getl (Function fd, c)) - | exception Not_found -> - (* unknown function *) - M.error ~category:Imprecise ~tags:[Category Unsound] "Created a thread from unknown function %s" f.vname - (* actual implementation (e.g. invalidation) is done by threadenter *) - ) ds - in - (* ... nice, right! *) - let pval = sync ctx in - { ctx with local = pval }, r, spawns - - let rec bigsqcup = function - | [] -> D.bot () - | [x] -> x - | x::xs -> D.join x (bigsqcup xs) - - let thread_spawns ctx d spawns = - if List.is_empty spawns then - d - else - let rec ctx' = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query ctx' q) - ; local = d - } - in - (* TODO: don't forget path dependencies *) - let one_spawn (lval, f, args, fd, multiple) = - let rec fctx = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query fctx q) - ; local = fd - } - in - S.threadspawn ctx' ~multiple lval f args fctx - in - bigsqcup (List.map one_spawn spawns) - - let common_join ctx d splits spawns = - thread_spawns ctx (bigsqcup (d :: splits)) spawns - - let common_joins ctx ds splits spawns = common_join ctx (bigsqcup ds) splits spawns - - let tf_assign var edge prev_node lv e getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.assign ctx lv e in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let tf_vdecl var edge prev_node v getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.vdecl ctx v in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let normal_return r fd ctx sideg = - let spawning_return = S.return ctx r fd in - let nval = S.sync { ctx with local = spawning_return } `Return in - nval - - let toplevel_kernel_return r fd ctx sideg = - let st = if fd.svar.vname = MyCFG.dummy_func.svar.vname then ctx.local else S.return ctx r fd in - let spawning_return = S.return {ctx with local = st} None MyCFG.dummy_func in - let nval = S.sync { ctx with local = spawning_return } `Return in - nval - - let tf_ret var edge prev_node ret fd getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - if (CilType.Fundec.equal fd MyCFG.dummy_func || - List.mem fd.svar.vname (get_string_list "mainfun")) && - get_bool "kernel" - then toplevel_kernel_return ret fd ctx sideg - else normal_return ret fd ctx sideg - in - common_join ctx d !r !spawns - - let tf_entry var edge prev_node fd getl sidel getg sideg d = - (* Side effect function context here instead of at sidel to FunctionEntry, - because otherwise context for main functions (entrystates) will be missing or pruned during postsolving. *) - let c: unit -> S.C.t = snd var |> Obj.obj in - side_context sideg fd (c ()); - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.body ctx fd in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let tf_test var edge prev_node e tv getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.branch ctx e tv in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let tf_normal_call ctx lv e (f:fundec) args getl sidel getg sideg = - let combine (cd, fc, fd) = - if M.tracing then M.traceli "combine" "local: %a" S.D.pretty cd; - if M.tracing then M.trace "combine" "function: %a" S.D.pretty fd; - let rec cd_ctx = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); - local = cd; - } - in - let fd_ctx = - (* Inner scope to prevent unsynced fd_ctx from being used. *) - (* Extra sync in case function has multiple returns. - Each `Return sync is done before joining, so joined value may be unsound. - Since sync is normally done before tf (in common_ctx), simulate it here for fd. *) - (* TODO: don't do this extra sync here *) - let rec sync_ctx = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query sync_ctx q); - local = fd; - prev_node = Function f; - } - in - (* TODO: more accurate ctx? *) - let synced = sync sync_ctx in - let rec fd_ctx = - { sync_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query fd_ctx q); - local = synced; - } - in - fd_ctx - in - let r = List.fold_left (fun acc fd1 -> - let rec fd1_ctx = - { fd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query fd1_ctx q); - local = fd1; - } - in - let combine_enved = S.combine_env cd_ctx lv e f args fc fd1_ctx.local (Analyses.ask_of_ctx fd1_ctx) in - let rec combine_assign_ctx = - { cd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query combine_assign_ctx q); - local = combine_enved; - } - in - S.D.join acc (S.combine_assign combine_assign_ctx lv e f args fc fd1_ctx.local (Analyses.ask_of_ctx fd1_ctx)) - ) (S.D.bot ()) (S.paths_as_set fd_ctx) - in - if M.tracing then M.traceu "combine" "combined local: %a" S.D.pretty r; - r - in - let paths = S.enter ctx lv f args in - let paths = List.map (fun (c,v) -> (c, S.context ctx f v, v)) paths in - List.iter (fun (c,fc,v) -> if not (S.D.is_bot v) then sidel (FunctionEntry f, fc) v) paths; - let paths = List.map (fun (c,fc,v) -> (c, fc, if S.D.is_bot v then v else getl (Function f, fc))) paths in - (* Don't filter bot paths, otherwise LongjmpLifter is not called. *) - (* let paths = List.filter (fun (c,fc,v) -> not (D.is_bot v)) paths in *) - let paths = List.map (Tuple3.map2 Option.some) paths in - if M.tracing then M.traceli "combine" "combining"; - let paths = List.map combine paths in - let r = List.fold_left D.join (D.bot ()) paths in - if M.tracing then M.traceu "combine" "combined: %a" S.D.pretty r; - r - - let tf_special_call ctx lv f args = S.special ctx lv f args - - let tf_proc var edge prev_node lv e args getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let functions = - match e with - | Lval (Var v, NoOffset) -> - (* Handle statically known function call directly. - Allows deactivating base. *) - [v] - | _ -> - (* Depends on base for query. *) - let ad = ctx.ask (Queries.EvalFunvar e) in - Queries.AD.to_var_may ad (* TODO: don't convert, handle UnknownPtr below *) - in - let one_function f = - match f.vtype with - | TFun (_, params, var_arg, _) -> - let arg_length = List.length args in - let p_length = Option.map_default List.length 0 params in - (* Check whether number of arguments fits. *) - (* If params is None, the function or its parameters are not declared, so we still analyze the unknown function call. *) - if Option.is_none params || p_length = arg_length || (var_arg && arg_length >= p_length) then - begin Some (match Cilfacade.find_varinfo_fundec f with - | fd when LibraryFunctions.use_special f.vname -> - M.info ~category:Analyzer "Using special for defined function %s" f.vname; - tf_special_call ctx lv f args - | fd -> - tf_normal_call ctx lv e fd args getl sidel getg sideg - | exception Not_found -> - tf_special_call ctx lv f args) - end - else begin - let geq = if var_arg then ">=" else "" in - M.warn ~category:Unsound ~tags:[Category Call; CWE 685] "Potential call to function %a with wrong number of arguments (expected: %s%d, actual: %d). This call will be ignored." CilType.Varinfo.pretty f geq p_length arg_length; - None - end - | _ -> - M.warn ~category:Call "Something that is not a function (%a) is called." CilType.Varinfo.pretty f; - None - in - let funs = List.filter_map one_function functions in - if [] = funs && not (S.D.is_bot ctx.local) then begin - M.msg_final Warning ~category:Unsound ~tags:[Category Call] "No suitable function to call"; - M.warn ~category:Unsound ~tags:[Category Call] "No suitable function to be called at call site. Continuing with state before call."; - d (* because LevelSliceLifter *) - end else - common_joins ctx funs !r !spawns - - let tf_asm var edge prev_node getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.asm ctx in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let tf_skip var edge prev_node getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.skip ctx in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns - - let tf var getl sidel getg sideg prev_node edge d = - begin match edge with - | Assign (lv,rv) -> tf_assign var edge prev_node lv rv - | VDecl (v) -> tf_vdecl var edge prev_node v - | Proc (r,f,ars) -> tf_proc var edge prev_node r f ars - | Entry f -> tf_entry var edge prev_node f - | Ret (r,fd) -> tf_ret var edge prev_node r fd - | Test (p,b) -> tf_test var edge prev_node p b - | ASM (_, _, _) -> tf_asm var edge prev_node (* TODO: use ASM fields for something? *) - | Skip -> tf_skip var edge prev_node - end getl sidel getg sideg d - - type Goblint_backtrace.mark += TfLocation of location - - let () = Goblint_backtrace.register_mark_printer (function - | TfLocation loc -> - Some ("transfer function at " ^ CilType.Location.show loc) - | _ -> None (* for other marks *) - ) - - let tf var getl sidel getg sideg prev_node (_,edge) d (f,t) = - let old_loc = !Goblint_tracing.current_loc in - let old_loc2 = !Goblint_tracing.next_loc in - Goblint_tracing.current_loc := f; - Goblint_tracing.next_loc := t; - Goblint_backtrace.protect ~mark:(fun () -> TfLocation f) ~finally:(fun () -> - Goblint_tracing.current_loc := old_loc; - Goblint_tracing.next_loc := old_loc2 - ) (fun () -> - let d = tf var getl sidel getg sideg prev_node edge d in - d - ) - - let tf (v,c) (edges, u) getl sidel getg sideg = - let pval = getl (u,c) in - let _, locs = List.fold_right (fun (f,e) (t,xs) -> f, (f,t)::xs) edges (Node.location v,[]) in - List.fold_left2 (|>) pval (List.map (tf (v,Obj.repr (fun () -> c)) getl sidel getg sideg u) edges) locs - - let tf (v,c) (e,u) getl sidel getg sideg = - let old_node = !current_node in - let old_fd = Option.map Node.find_fundec old_node |? Cil.dummyFunDec in - let new_fd = Node.find_fundec v in - if not (CilType.Fundec.equal old_fd new_fd) then - Timing.Program.enter new_fd.svar.vname; - let old_context = !M.current_context in - current_node := Some u; - M.current_context := Some (Obj.magic c); (* magic is fine because Spec is top-level Control Spec *) - Fun.protect ~finally:(fun () -> - current_node := old_node; - M.current_context := old_context; - if not (CilType.Fundec.equal old_fd new_fd) then - Timing.Program.exit new_fd.svar.vname - ) (fun () -> - let d = tf (v,c) (e,u) getl sidel getg sideg in - d - ) - - let system (v,c) = - match v with - | FunctionEntry _ -> - None - | _ -> - let tf getl sidel getg sideg = - let tf' eu = tf (v,c) eu getl sidel getg sideg in - - match NodeH.find_option CfgTools.node_scc_global v with - | Some scc when NodeH.mem scc.prev v && NodeH.length scc.prev = 1 -> - (* Limited to loops with only one entry node. Otherwise unsound as is. *) - (* TODO: Is it possible to do soundly for multi-entry loops? *) - let stricts = NodeH.find_default scc.prev v [] in - let xs_stricts = List.map tf' stricts in - (* Evaluate non-strict for dead code warnings. See 00-sanity/36-strict-loop-dead. *) - let equal = [%eq: (CilType.Location.t * Edge.t) list * Node.t] in - let is_strict eu = List.exists (equal eu) stricts in - let non_stricts = List.filter (neg is_strict) (Cfg.prev v) in - let xs_non_stricts = List.map tf' non_stricts in - if List.for_all S.D.is_bot xs_stricts then - S.D.bot () - else ( - let xs_strict = List.fold_left S.D.join (S.D.bot ()) xs_stricts in - List.fold_left S.D.join xs_strict xs_non_stricts - ) - | _ -> - let xs = List.map tf' (Cfg.prev v) in - List.fold_left S.D.join (S.D.bot ()) xs - in - Some tf - - let iter_vars getl getg vq fl fg = - (* vars for Spec *) - let rec ctx = - { ask = (fun (type a) (q: a Queries.t) -> S.query ctx q) - ; emit = (fun _ -> failwith "Cannot \"emit\" in query context.") - ; node = MyCFG.dummy_node (* TODO maybe ask should take a node (which could be used here) instead of a location *) - ; prev_node = MyCFG.dummy_node - ; control_context = (fun () -> ctx_failwith "No context in query context.") - ; context = (fun () -> ctx_failwith "No context in query context.") - ; edge = MyCFG.Skip - ; local = S.startstate Cil.dummyFunDec.svar (* bot and top both silently raise and catch Deadcode in DeadcodeLifter *) - ; global = (fun g -> G.spec (getg (GVar.spec g))) - ; spawn = (fun ?(multiple=false) v d -> failwith "Cannot \"spawn\" in query context.") - ; split = (fun d es -> failwith "Cannot \"split\" in query context.") - ; sideg = (fun v g -> failwith "Cannot \"split\" in query context.") - } - in - let f v = fg (GVar.spec (Obj.obj v)) in - S.query ctx (IterSysVars (vq, f)); - - (* node vars for locals *) - match vq with - | Node {node; fundec} -> - let fd = Option.default_delayed (fun () -> Node.find_fundec node) fundec in - let cs = G.contexts (getg (GVar.contexts fd)) in - G.CSet.iter (fun c -> - fl (node, c) - ) cs - | _ -> - () - - let sys_change getl getg = - let open CompareCIL in - - let c = match I.increment with - | Some {changes; _} -> changes - | None -> empty_change_info () - in - List.(Logs.info "change_info = { unchanged = %d; changed = %d (with unchangedHeader = %d); added = %d; removed = %d }" (length c.unchanged) (length c.changed) (BatList.count_matching (fun c -> c.unchangedHeader) c.changed) (length c.added) (length c.removed)); - - let changed_funs = List.filter_map (function - | {old = {def = Some (Fun f); _}; diff = None; _} -> - Logs.info "Completely changed function: %s" f.svar.vname; - Some f - | _ -> None - ) c.changed - in - let part_changed_funs = List.filter_map (function - | {old = {def = Some (Fun f); _}; diff = Some nd; _} -> - Logs.info "Partially changed function: %s" f.svar.vname; - Some (f, nd.primObsoleteNodes, nd.unchangedNodes) - | _ -> None - ) c.changed - in - let removed_funs = List.filter_map (function - | {def = Some (Fun f); _} -> - Logs.info "Removed function: %s" f.svar.vname; - Some f - | _ -> None - ) c.removed - in - - let module HM = Hashtbl.Make (Var2 (LVar) (GVar)) in - - let mark_node hm f node = - iter_vars getl getg (Node {node; fundec = Some f}) (fun v -> - HM.replace hm (`L v) () - ) (fun v -> - HM.replace hm (`G v) () - ) - in - - let reluctant = GobConfig.get_bool "incremental.reluctant.enabled" in - let reanalyze_entry f = - (* destabilize the entry points of a changed function when reluctant is off, - or the function is to be force-reanalyzed *) - (not reluctant) || CompareCIL.VarinfoSet.mem f.svar c.exclude_from_rel_destab - in - let obsolete_ret = HM.create 103 in - let obsolete_entry = HM.create 103 in - let obsolete_prim = HM.create 103 in - - (* When reluctant is on: - Only add function entry nodes to obsolete_entry if they are in force-reanalyze *) - List.iter (fun f -> - if reanalyze_entry f then - (* collect function entry for eager destabilization *) - mark_node obsolete_entry f (FunctionEntry f) - else - (* collect function return for reluctant analysis *) - mark_node obsolete_ret f (Function f) - ) changed_funs; - (* Primary changed unknowns from partially changed functions need only to be collected for eager destabilization when reluctant is off *) - (* The return nodes of partially changed functions are collected in obsolete_ret for reluctant analysis *) - (* We utilize that force-reanalyzed functions are always considered as completely changed (and not partially changed) *) - List.iter (fun (f, pn, _) -> - if not reluctant then ( - List.iter (fun n -> - mark_node obsolete_prim f n - ) pn - ) - else - mark_node obsolete_ret f (Function f) - ) part_changed_funs; - - let obsolete = Enum.append (HM.keys obsolete_entry) (HM.keys obsolete_prim) |> List.of_enum in - let reluctant = HM.keys obsolete_ret |> List.of_enum in - - let marked_for_deletion = HM.create 103 in - - let dummy_pseudo_return_node f = - (* not the same as in CFG, but compares equal because of sid *) - Node.Statement ({Cil.dummyStmt with sid = Cilfacade.get_pseudo_return_id f}) - in - let add_nodes_of_fun (functions: fundec list) (withEntry: fundec -> bool) = - let add_stmts (f: fundec) = - List.iter (fun s -> - mark_node marked_for_deletion f (Statement s) - ) f.sallstmts - in - List.iter (fun f -> - if withEntry f then - mark_node marked_for_deletion f (FunctionEntry f); - mark_node marked_for_deletion f (Function f); - add_stmts f; - mark_node marked_for_deletion f (dummy_pseudo_return_node f) - ) functions; - in - - add_nodes_of_fun changed_funs reanalyze_entry; - add_nodes_of_fun removed_funs (fun _ -> true); - (* it is necessary to remove all unknowns for changed pseudo-returns because they have static ids *) - let add_pseudo_return f un = - let pseudo = dummy_pseudo_return_node f in - if not (List.exists (Node.equal pseudo % fst) un) then - mark_node marked_for_deletion f (dummy_pseudo_return_node f) - in - List.iter (fun (f,_,un) -> - mark_node marked_for_deletion f (Function f); - add_pseudo_return f un - ) part_changed_funs; - - let delete = HM.keys marked_for_deletion |> List.of_enum in - - let restart = match I.increment with - | Some data -> - let restart = ref [] in - List.iter (fun g -> - iter_vars getl getg g (fun v -> - restart := `L v :: !restart - ) (fun v -> - restart := `G v :: !restart - ) - ) data.restarting; - !restart - | None -> [] - in - - {obsolete; delete; reluctant; restart} -end - (** Add path sensitivity to a analysis *) module PathSensitive2 (Spec:Spec) From ab5a7b6e56aca81b70e7a1b913b39f78d0fdb377 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:05:57 +0300 Subject: [PATCH 377/566] Remove Spec lifters from Constraints --- src/framework/constraints.ml | 1168 ---------------------------------- 1 file changed, 1168 deletions(-) diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index a8728f2548..fb4b5081e8 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -8,503 +8,6 @@ open Analyses open ConstrSys open GobConfig -module M = Messages - - -(** Lifts a [Spec] so that the domain is [Hashcons]d *) -module HashconsLifter (S:Spec) - : Spec with module G = S.G - and module C = S.C -= -struct - module HConsedArg = - struct - (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) - (* see https://github.com/goblint/analyzer/issues/1005 *) - let assume_idempotent = GobConfig.get_string "ana.int.refinement" = "never" - end - module D = Lattice.HConsed (S.D) (HConsedArg) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt x = of_elt (D.unlift x) - end - - let name () = S.name () ^" hashconsed" - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init = S.init - let finalize = S.finalize - - let startstate v = D.lift (S.startstate v) - let exitstate v = D.lift (S.exitstate v) - let morphstate v d = D.lift (S.morphstate v (D.unlift d)) - - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) - } - - let context ctx fd = S.context (conv ctx) fd % D.unlift - let startcontext () = S.startcontext () - - let sync ctx reason = - D.lift @@ S.sync (conv ctx) reason - - let query ctx = - S.query (conv ctx) - - let assign ctx lv e = - D.lift @@ S.assign (conv ctx) lv e - - let vdecl ctx v = - D.lift @@ S.vdecl (conv ctx) v - - let branch ctx e tv = - D.lift @@ S.branch (conv ctx) e tv - - let body ctx f = - D.lift @@ S.body (conv ctx) f - - let return ctx r f = - D.lift @@ S.return (conv ctx) r f - - let asm ctx = - D.lift @@ S.asm (conv ctx) - - let skip ctx = - D.lift @@ S.skip (conv ctx) - - let enter ctx r f args = - List.map (fun (x,y) -> D.lift x, D.lift y) @@ S.enter (conv ctx) r f args - - let special ctx r f args = - D.lift @@ S.special (conv ctx) r f args - - let combine_env ctx r fe f args fc es f_ask = - D.lift @@ S.combine_env (conv ctx) r fe f args fc (D.unlift es) f_ask - - let combine_assign ctx r fe f args fc es f_ask = - D.lift @@ S.combine_assign (conv ctx) r fe f args fc (D.unlift es) f_ask - - let threadenter ctx ~multiple lval f args = - List.map D.lift @@ S.threadenter (conv ctx) ~multiple lval f args - - let threadspawn ctx ~multiple lval f args fctx = - D.lift @@ S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) - - let paths_as_set ctx = - List.map (fun x -> D.lift x) @@ S.paths_as_set (conv ctx) - - let event ctx e octx = - D.lift @@ S.event (conv ctx) e (conv octx) -end - -(** Lifts a [Spec] so that the context is [Hashcons]d. *) -module HashconsContextLifter (S:Spec) - : Spec with module D = S.D - and module G = S.G - and module C = Printable.HConsed (S.C) -= -struct - module D = S.D - module G = S.G - module C = Printable.HConsed (S.C) - module V = S.V - module P = S.P - - let name () = S.name () ^" context hashconsed" - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init = S.init - let finalize = S.finalize - - let startstate = S.startstate - let exitstate = S.exitstate - let morphstate = S.morphstate - - let conv ctx = - { ctx with context = (fun () -> C.unlift (ctx.context ())) } - - let context ctx fd = C.lift % S.context (conv ctx) fd - let startcontext () = C.lift @@ S.startcontext () - - let sync ctx reason = - S.sync (conv ctx) reason - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | Queries.IterPrevVars f -> - let g i (n, c, j) e = f i (n, Obj.repr (C.lift (Obj.obj c)), j) e in - S.query (conv ctx) (Queries.IterPrevVars g) - | _ -> S.query (conv ctx) q - - let assign ctx lv e = - S.assign (conv ctx) lv e - - let vdecl ctx v = - S.vdecl (conv ctx) v - - let branch ctx e tv = - S.branch (conv ctx) e tv - - let body ctx f = - S.body (conv ctx) f - - let return ctx r f = - S.return (conv ctx) r f - - let asm ctx = - S.asm (conv ctx) - - let skip ctx = - S.skip (conv ctx) - - let enter ctx r f args = - S.enter (conv ctx) r f args - - let special ctx r f args = - S.special (conv ctx) r f args - - let combine_env ctx r fe f args fc es f_ask = - S.combine_env (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - - let combine_assign ctx r fe f args fc es f_ask = - S.combine_assign (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - - let threadenter ctx ~multiple lval f args = - S.threadenter (conv ctx) ~multiple lval f args - - let threadspawn ctx ~multiple lval f args fctx = - S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) - - let paths_as_set ctx = S.paths_as_set (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - -(* see option ana.opt.equal *) -module OptEqual (S: Spec) = struct - module D = struct include S.D let equal x y = x == y || equal x y end - module G = struct include S.G let equal x y = x == y || equal x y end - module C = struct include S.C let equal x y = x == y || equal x y end - include (S : Spec with module D := D and module G := G and module C := C) -end - -(** If dbg.slice.on, stops entering functions after dbg.slice.n levels. *) -module LevelSliceLifter (S:Spec) - : Spec with module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) - and module G = S.G - and module C = S.C -= -struct - module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - let name () = S.name ()^" level sliced" - - let start_level = ref (`Top) - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init marshal = - if get_bool "dbg.slice.on" then - start_level := `Lifted (Int64.of_int (get_int "dbg.slice.n")); - S.init marshal - - let finalize = S.finalize - - let startstate v = (S.startstate v, !start_level) - let exitstate v = (S.exitstate v, !start_level) - let morphstate v (d,l) = (S.morphstate v d, l) - - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) - } - - let context ctx fd (d,_) = S.context (conv ctx) fd d - let startcontext () = S.startcontext () - - let lift_fun ctx f g h = - f @@ h (g (conv ctx)) - - let enter' ctx r f args = - let liftmap = List.map (fun (x,y) -> (x, snd ctx.local), (y, snd ctx.local)) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) - - let lift ctx d = (d, snd ctx.local) - let lift_start_level d = (d, !start_level) - - let sync ctx reason = lift_fun ctx (lift ctx) S.sync ((|>) reason) - let query' ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun x -> x q) - let assign ctx lv e = lift_fun ctx (lift ctx) S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx (lift ctx) S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx (lift ctx) S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx (lift ctx) S.body ((|>) f) - let return ctx r f = lift_fun ctx (lift ctx) S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx (lift ctx) S.asm identity - let skip ctx = lift_fun ctx (lift ctx) S.skip identity - let special ctx r f args = lift_fun ctx (lift ctx) S.special ((|>) args % (|>) f % (|>) r) - let combine_env' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (lift ctx) (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - - let leq0 = function - | `Top -> false - | `Lifted x -> x <= 0L - | `Bot -> true - - let sub1 = function - | `Lifted x -> `Lifted (Int64.sub x 1L) - | x -> x - - let add1 = function - | `Lifted x -> `Lifted (Int64.add x 1L) - | x -> x - - let paths_as_set ctx = - let liftmap = List.map (fun x -> (x, snd ctx.local)) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) - - let event ctx e octx = - lift_fun ctx (lift ctx) S.event ((|>) (conv octx) % (|>) e) - - let enter ctx r f args = - let (d,l) = ctx.local in - if leq0 l then - [ctx.local, D.bot ()] - else - enter' {ctx with local=(d, sub1 l)} r f args - - let combine_env ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in - let l = add1 l in - if leq0 l then - (d, l) - else - let d',_ = combine_env' ctx r fe f args fc es f_ask in - (d', l) - - let combine_assign ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in - (* No need to add1 here, already done in combine_env. *) - if leq0 l then - (d, l) - else - let d',_ = combine_assign' ctx r fe f args fc es f_ask in - (d', l) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | Queries.EvalFunvar e -> - let (d,l) = ctx.local in - if leq0 l then - Queries.AD.empty () - else - query' ctx (Queries.EvalFunvar e) - | q -> query' ctx q -end - - -(** Limits the number of widenings per node. *) -module LimitLifter (S:Spec) = -struct - include (S : module type of S with module D := S.D and type marshal = S.marshal) - - let name () = S.name ()^" limited" - - let limit = ref 0 - - let init marshal = - limit := get_int "dbg.limit.widen"; - S.init marshal - - module H = MyCFG.NodeH - let h = H.create 13 - let incr k = - H.modify_def 1 k (fun v -> - if v >= !limit then failwith (GobPretty.sprintf "LimitLifter: Reached limit (%d) for node %a" !limit Node.pretty_plain_short (Option.get !MyCFG.current_node)); - v+1 - ) h; - module D = struct - include S.D - let widen x y = Option.may incr !MyCFG.current_node; widen x y (* when is this None? *) - end -end - - -(* widening on contexts, keeps contexts for calls only in D *) -module WidenContextLifterSide (S:Spec) -= -struct - module DD = - struct - include S.D - let printXml f d = BatPrintf.fprintf f "%a" printXml d - end - module M = MapDomain.MapBot (Basetype.Variables) (DD) (* should be CilFun -> S.C, but CilFun is not Groupable, and S.C is no Lattice *) - - module D = struct - include Lattice.Prod (S.D) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.D.printXml d M.printXml m - end - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - - let name () = S.name ()^" with widened contexts" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - let inj f x = f x, M.bot () - - let startcontext () = S.startcontext () - let startstate = inj S.startstate - let exitstate = inj S.exitstate - let morphstate v (d,m) = S.morphstate v d, m - - - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) - } - - let context ctx fd (d,m) = S.context (conv ctx) fd d (* just the child analysis' context *) - - let lift_fun ctx f g = g (f (conv ctx)), snd ctx.local - - let sync ctx reason = lift_fun ctx S.sync ((|>) reason) - let query ctx = S.query (conv ctx) - let assign ctx lv e = lift_fun ctx S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx S.body ((|>) f) - let return ctx r f = lift_fun ctx S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx S.asm identity - let skip ctx = lift_fun ctx S.skip identity - let special ctx r f args = lift_fun ctx S.special ((|>) args % (|>) f % (|>) r) - - let event ctx e octx = lift_fun ctx S.event ((|>) (conv octx) % (|>) e) - - let threadenter ctx ~multiple lval f args = S.threadenter (conv ctx) ~multiple lval f args |> List.map (fun d -> (d, snd ctx.local)) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - - let enter ctx r f args = - let m = snd ctx.local in - let d' v_cur = - if ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.context.widen" ~keepAttr:"widen" ~removeAttr:"no-widen" f then ( - let v_old = M.find f.svar m in (* S.D.bot () if not found *) - let v_new = S.D.widen v_old (S.D.join v_old v_cur) in - Messages.(if tracing && not (S.D.equal v_old v_new) then tracel "widen-context" "enter results in new context for function %s" f.svar.vname); - v_new, M.add f.svar v_new m - ) - else - v_cur, m - in - S.enter (conv ctx) r f args - |> List.map (fun (c,v) -> (c,m), d' v) (* c: caller, v: callee *) - - let paths_as_set ctx = - let m = snd ctx.local in - S.paths_as_set (conv ctx) |> List.map (fun v -> (v,m)) - - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) -end - - -(** Lifts a [Spec] with a special bottom element that represent unreachable code. *) -module DeadCodeLifter (S:Spec) - : Spec with module D = Dom (S.D) - and module G = S.G - and module C = S.C -= -struct - module D = Dom (S.D) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include Printable.Option (S.P) (struct let name = "None" end) - - let of_elt = function - | `Lifted x -> Some (S.P.of_elt x) - | _ -> None - end - - let name () = S.name ()^" lifted" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - - let startcontext () = S.startcontext () - let startstate v = `Lifted (S.startstate v) - let exitstate v = `Lifted (S.exitstate v) - let morphstate v d = try `Lifted (S.morphstate v (D.unlift d)) with Deadcode -> d - - - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) - } - - let context ctx fd = S.context (conv ctx) fd % D.unlift - - let lift_fun ctx f g h b = - try f @@ h (g (conv ctx)) - with Deadcode -> b - - let sync ctx reason = lift_fun ctx D.lift S.sync ((|>) reason) `Bot - - let enter ctx r f args = - let liftmap = List.map (fun (x,y) -> D.lift x, D.lift y) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) [] - - let paths_as_set ctx = - let liftmap = List.map (fun x -> D.lift x) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) [D.bot ()] (* One dead path instead of none, such that combine_env gets called for functions with dead normal return (and thus longjmpy returns can be correctly handled by lifter). *) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun (x) -> x q) (Queries.Result.bot q) - let assign ctx lv e = lift_fun ctx D.lift S.assign ((|>) e % (|>) lv) `Bot - let vdecl ctx v = lift_fun ctx D.lift S.vdecl ((|>) v) `Bot - let branch ctx e tv = lift_fun ctx D.lift S.branch ((|>) tv % (|>) e) `Bot - let body ctx f = lift_fun ctx D.lift S.body ((|>) f) `Bot - let return ctx r f = lift_fun ctx D.lift S.return ((|>) f % (|>) r) `Bot - let asm ctx = lift_fun ctx D.lift S.asm identity `Bot - let skip ctx = lift_fun ctx D.lift S.skip identity `Bot - let special ctx r f args = lift_fun ctx D.lift S.special ((|>) args % (|>) f % (|>) r) `Bot - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx D.lift (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot - - let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot -end module type Increment = sig @@ -1037,674 +540,3 @@ struct {obsolete; delete; reluctant; restart} end - - -(** Add path sensitivity to a analysis *) -module PathSensitive2 (Spec:Spec) - : Spec - with module G = Spec.G - and module C = Spec.C - and module V = Spec.V -= -struct - module D = - struct - (* TODO is it really worth it to check every time instead of just using sets and joining later? *) - module R = - struct - include Spec.P - type elt = Spec.D.t - end - module J = SetDomain.Joined (Spec.D) - include DisjointDomain.ProjectiveSet (Spec.D) (J) (R) - let name () = "PathSensitive (" ^ name () ^ ")" - - let printXml f x = - let print_one x = - BatPrintf.fprintf f "\n%a" Spec.D.printXml x - in - iter print_one x - end - - module G = Spec.G - module C = Spec.C - module V = Spec.V - module P = UnitP - - let name () = "PathSensitive2("^Spec.name ()^")" - - type marshal = Spec.marshal - let init = Spec.init - let finalize = Spec.finalize - - let startcontext () = Spec.startcontext () - let exitstate v = D.singleton (Spec.exitstate v) - let startstate v = D.singleton (Spec.startstate v) - let morphstate v d = D.map (Spec.morphstate v) d - - let conv ctx x = - let rec ctx' = { ctx with ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx' q) - ; local = x - ; split = (ctx.split % D.singleton) } - in - ctx' - - let context ctx fd l = - if D.cardinal l <> 1 then - failwith "PathSensitive2.context must be called with a singleton set." - else - let x = D.choose l in - Spec.context (conv ctx x) fd x - - - let map ctx f g = - let h x xs = - try D.add (g (f (conv ctx x))) xs - with Deadcode -> xs - in - let d = D.fold h ctx.local (D.empty ()) in - if D.is_bot d then raise Deadcode else d - - let fold' ctx f g h a = - let k x a = - try h a @@ g @@ f @@ conv ctx x - with Deadcode -> a - in - D.fold k ctx.local a - - let assign ctx l e = map ctx Spec.assign (fun h -> h l e ) - let vdecl ctx v = map ctx Spec.vdecl (fun h -> h v) - let body ctx f = map ctx Spec.body (fun h -> h f ) - let return ctx e f = map ctx Spec.return (fun h -> h e f ) - let branch ctx e tv = map ctx Spec.branch (fun h -> h e tv) - let asm ctx = map ctx Spec.asm identity - let skip ctx = map ctx Spec.skip identity - let special ctx l f a = map ctx Spec.special (fun h -> h l f a) - - let event ctx e octx = - let fd1 = D.choose octx.local in - map ctx Spec.event (fun h -> h e (conv octx fd1)) - - let threadenter ctx ~multiple lval f args = - let g xs ys = (List.map (fun y -> D.singleton y) ys) @ xs in - fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] - - let threadspawn ctx ~multiple lval f args fctx = - let fd1 = D.choose fctx.local in - map ctx (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fctx fd1)) - - let sync ctx reason = map ctx Spec.sync (fun h -> h reason) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - (* TODO: handle Invariant path like PathSensitive3? *) - (* join results so that they are sound for all paths *) - let module Result = (val Queries.Result.lattice q) in - fold' ctx Spec.query identity (fun x f -> Result.join x (f q)) (Result.bot ()) - - let enter ctx l f a = - let g xs ys = (List.map (fun (x,y) -> D.singleton x, D.singleton y) ys) @ xs in - fold' ctx Spec.enter (fun h -> h l f a) g [] - - let paths_as_set ctx = - (* Path-sensitivity is only here, not below! *) - let elems = D.elements ctx.local in - List.map (D.singleton) elems - - let combine_env ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in - let k x y = - if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; - try - let r = Spec.combine_env (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; - D.add r y - with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead"; - y - in - let d = D.fold k d (D.bot ()) in - if D.is_bot d then raise Deadcode else d - - let combine_assign ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in - let k x y = - if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; - try - let r = Spec.combine_assign (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; - D.add r y - with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead"; - y - in - let d = D.fold k d (D.bot ()) in - if D.is_bot d then raise Deadcode else d -end - -module DeadBranchLifter (S: Spec): Spec = -struct - include S - - let name () = "DeadBranch (" ^ S.name () ^ ")" - - (* Two global invariants: - 1. S.V -> S.G -- used for S - 2. node -> (exp -> flat bool) -- used for warnings *) - - module V = - struct - include Printable.EitherConf (struct let expand1 = false let expand2 = true end) (S.V) (Node) - let name () = "DeadBranch" - let s x = `Left x - let node x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | `Right _ -> true - end - - module EM = - struct - include MapDomain.MapBot (Basetype.CilExp) (BoolDomain.FlatBool) - let name () = "branches" - end - - module G = - struct - include Lattice.Lift2 (S.G) (EM) - let name () = "deadbranch" - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "DeadBranchLifter.s" - let node = function - | `Bot -> EM.bot () - | `Lifted2 x -> x - | _ -> failwith "DeadBranchLifter.node" - let create_s s = `Lifted1 s - let create_node node = `Lifted2 node - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> - let em = G.node (ctx.global (V.node g)) in - EM.iter (fun exp tv -> - match tv with - | `Lifted tv -> - let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) - let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in - M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv - | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) - M.msg_final Error ~category:Analyzer ~tags:[Category Unsound] "Both branches dead"; - M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp - | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) - | `Top -> (* may be both true and false *) - () - ) em; - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - - (* node vars for dead branches *) - begin match vq with - | Node {node; _} -> - vf (Obj.repr (V.node node)) - | _ -> - () - end - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - let context ctx = S.context (conv ctx) - - let branch ctx exp tv = - if !AnalysisState.postsolving then ( - try - let r = branch ctx exp tv in - (* branch is live *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) - r - with Deadcode -> - (* branch is dead *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) - raise Deadcode - ) - else ( - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) - branch ctx exp tv - ) - - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - -module LongjmpLifter (S: Spec): Spec = -struct - include S - - let name () = "Longjmp (" ^ S.name () ^ ")" - - module V = - struct - include Printable.Either3Conf (struct let expand1 = false let expand2 = true let expand3 = true end) (S.V) (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C)) - let name () = "longjmp" - let s x = `Left x - let longjmpto x = `Middle x - let longjmpret x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | _ -> false - end - - module G = - struct - include Lattice.Lift2 (S.G) (S.D) - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "LongjmpLifter.s" - let local = function - | `Bot -> S.D.bot () - | `Lifted2 x -> x - | _ -> failwith "LongjmpLifter.local" - let create_s s = `Lifted1 s - let create_local local = `Lifted2 local - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" S.D.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | _ -> - Queries.Result.top q - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | _ -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - (* TODO: vars? *) - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let context ctx = S.context (conv ctx) - - let combine_env ctx lv e f args fc fd f_ask = - let conv_ctx = conv ctx in - let current_fundec = Node.find_fundec ctx.node in - let handle_longjmp (cd, fc, longfd) = - (* This is called per-path. *) - let rec cd_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); - local = cd; - } - in - let longfd_ctx = - (* Inner scope to prevent unsynced longfd_ctx from being used. *) - (* Extra sync like with normal combine. *) - let rec sync_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query sync_ctx q); - local = longfd; - prev_node = Function f; - } - in - let synced = S.sync sync_ctx `Join in - let rec longfd_ctx = - { sync_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query longfd_ctx q); - local = synced; - } - in - longfd_ctx - in - let combined = lazy ( (* does not depend on target, do at most once *) - (* Globals are non-problematic here, as they are always carried around without any issues! *) - (* A combine call is mostly needed to ensure locals have appropriate values. *) - (* Using f from called function on purpose here! Needed? *) - S.combine_env cd_ctx None e f args fc longfd_ctx.local (Analyses.ask_of_ctx longfd_ctx) (* no lval because longjmp return skips return value assignment *) - ) - in - let returned = lazy ( (* does not depend on target, do at most once *) - let rec combined_ctx = - { cd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query combined_ctx q); - local = Lazy.force combined; - } - in - S.return combined_ctx None current_fundec - ) - in - let (active_targets, _) = longfd_ctx.ask ActiveJumpBuf in - let valid_targets = cd_ctx.ask ValidLongJmp in - let handle_target target = match target with - | JmpBufDomain.BufferEntryOrTop.AllTargets -> () (* The warning is already emitted at the point where the longjmp happens *) - | Target (target_node, target_context) -> - let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Fun: Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force combined)) - (* No need to propagate this outwards here, the set of valid longjumps is part of the context, we can never have the same context setting the longjmp multiple times *) - ) - (* Appropriate setjmp is not in current function & current context *) - else if JmpBufDomain.JmpBufSet.mem target valid_targets then - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) - else - (* It actually is not handled here but was propagated here spuriously, we already warned at the location where this issue is caused *) - (* As the validlongjumps inside the callee is a a superset of the ones inside the caller *) - () - in - JmpBufDomain.JmpBufSet.iter handle_target active_targets - in - if M.tracing then M.tracel "longjmp" "longfd getg %a" CilType.Fundec.pretty f; - let longfd = G.local (ctx.global (V.longjmpret (f, Option.get fc))) in - if M.tracing then M.tracel "longjmp" "longfd %a" D.pretty longfd; - if not (D.is_bot longfd) then - handle_longjmp (ctx.local, fc, longfd); - S.combine_env (conv_ctx) lv e f args fc fd f_ask - - let combine_assign ctx lv e f args fc fd f_ask = - S.combine_assign (conv ctx) lv e f args fc fd f_ask - - let special ctx lv f args = - let conv_ctx = conv ctx in - match (LibraryFunctions.find f).special args with - | Setjmp {env} -> - (* Handling of returning for the first time *) - let normal_return = S.special conv_ctx lv f args in - let jmp_return = G.local (ctx.global (V.longjmpto (ctx.prev_node, ctx.context ()))) in - if S.D.is_bot jmp_return then - normal_return - else ( - let rec jmp_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query jmp_ctx q); - local = jmp_return; - } - in - let longjmped = S.event jmp_ctx (Events.Longjmped {lval=lv}) jmp_ctx in - S.D.join normal_return longjmped - ) - | Longjmp {env; value} -> - let current_fundec = Node.find_fundec ctx.node in - let handle_path path = ( - let rec path_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query path_ctx q); - local = path; - } - in - let specialed = lazy ( (* does not depend on target, do at most once *) - S.special path_ctx lv f args - ) - in - let returned = lazy ( (* does not depend on target, do at most once *) - let rec specialed_ctx = - { path_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query specialed_ctx q); - local = Lazy.force specialed; - } - in - S.return specialed_ctx None current_fundec - ) - in - (* Eval `env` again to avoid having to construct bespoke ctx to ask *) - let targets = path_ctx.ask (EvalJumpBuf env) in - let valid_targets = path_ctx.ask ValidLongJmp in - if M.tracing then Messages.tracel "longjmp" "Jumping to %a" JmpBufDomain.JmpBufSet.pretty targets; - let handle_target target = match target with - | JmpBufDomain.BufferEntryOrTop.AllTargets -> - M.warn ~category:Imprecise "Longjmp to potentially invalid target, as contents of buffer %a may be unknown! (imprecision due to heap?)" d_exp env; - M.msg_final Error ~category:Unsound ~tags:[Category Imprecise; Category Call] "Longjmp to unknown target ignored" - | Target (target_node, target_context) -> - let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force specialed)) - ) - else if JmpBufDomain.JmpBufSet.mem target valid_targets then ( - if M.tracing then Messages.tracel "longjmp" "Longjmp to somewhere else, side-effect to %i" (S.C.hash (ctx.context ())); - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) - ) - else - M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target! (Target %a in Function %a which may have already returned or is in a different thread)" Node.pretty target_node CilType.Fundec.pretty target_fundec - in - if JmpBufDomain.JmpBufSet.is_empty targets then - M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target (%a is bot?!)" d_exp env - else - JmpBufDomain.JmpBufSet.iter handle_target targets - ) - in - List.iter handle_path (S.paths_as_set conv_ctx); - if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( - AnalysisState.svcomp_may_not_terminate := true; - M.warn ~category:Termination "The program might not terminate! (Longjmp)" - ); - S.D.bot () - | _ -> S.special conv_ctx lv f args - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - - -(** Add cycle detection in the context-sensitive dynamic function call graph to an analysis *) -module RecursionTermLifter (S: Spec) - : Spec with module D = S.D - and module C = S.C -= -(* two global invariants: - - S.V -> S.G - Needed to store the previously built global invariants - - fundec * S.C -> (Set (fundec * S.C)) - The second global invariant maps from the callee fundec and context to a set of caller fundecs and contexts. - This structure therefore stores the context-sensitive call graph. - For example: - let the function f in context c call function g in context c'. - In the global invariant structure it would be stored like this: (g,c') -> {(f, c)} -*) - -struct - include S - - (* contains all the callee fundecs and contexts *) - module V = GVarFC(S.V)(S.C) - - (* Tuple containing the fundec and context of a caller *) - module Call = Printable.Prod (CilType.Fundec) (S.C) - - (* Set containing multiple caller tuples *) - module CallerSet = SetDomain.Make (Call) - - module G = - struct - include Lattice.Lift2 (G) (CallerSet) - - let spec = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "RecursionTermLifter.spec" - - let callers = function - | `Bot -> CallerSet.bot () - | `Lifted2 x -> x - | _ -> failwith "RecursionTermLifter.callGraph" - - let create_spec spec = `Lifted1 spec - let create_singleton_caller caller = `Lifted2 (CallerSet.singleton caller) - - let printXml f = function - | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CallerSet.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - - end - - let name () = "RecursionTermLifter (" ^ S.name () ^ ")" - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.spec (ctx.global (V.spec v))); - sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_spec g)); - } - - let cycleDetection ctx call = - let module LH = Hashtbl.Make (Printable.Prod (CilType.Fundec) (S.C)) in - let module LS = Set.Make (Printable.Prod (CilType.Fundec) (S.C)) in - (* find all cycles/SCCs *) - let global_visited_calls = LH.create 100 in - - (* DFS *) - let rec iter_call (path_visited_calls: LS.t) ((fundec, _) as call) = - if LS.mem call path_visited_calls then ( - AnalysisState.svcomp_may_not_terminate := true; (*set the indicator for a non-terminating program for the sv comp*) - (*Cycle found*) - let loc = M.Location.CilLocation fundec.svar.vdecl in - M.warn ~loc ~category:Termination "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec) (* output a warning for non-termination*) - else if not (LH.mem global_visited_calls call) then begin - LH.replace global_visited_calls call (); - let new_path_visited_calls = LS.add call path_visited_calls in - let gvar = V.call call in - let callers = G.callers (ctx.global gvar) in - CallerSet.iter (fun to_call -> - iter_call new_path_visited_calls to_call - ) callers; - end - in - iter_call LS.empty call - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal v -> - (* check result of loop analysis *) - if not (ctx.ask Queries.MustTermAllLoops) then - AnalysisState.svcomp_may_not_terminate := true; - let v: V.t = Obj.obj v in - begin match v with - | `Left v' -> - S.query (conv ctx) (WarnGlobal (Obj.repr v')) - | `Right call -> cycleDetection ctx call (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) - end - | InvariantGlobal v -> - let v: V.t = Obj.obj v in - begin match v with - | `Left v -> - S.query (conv ctx) (InvariantGlobal (Obj.repr v)) - | `Right v -> - Queries.Result.top q - end - | _ -> S.query (conv ctx) q - - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - - - let record_call sideg callee caller = - sideg (V.call callee) (G.create_singleton_caller caller) - - let enter ctx = S.enter (conv ctx) - let context ctx = S.context (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx r fe f args fc es f_ask = - if !AnalysisState.postsolving then ( - let c_r: S.C.t = ctx.context () in (* Caller context *) - let nodeF = ctx.node in - let fd_r : fundec = Node.find_fundec nodeF in (* Caller fundec *) - let caller: (fundec * S.C.t) = (fd_r, c_r) in - let c_e: S.C.t = Option.get fc in (* Callee context *) - let fd_e : fundec = f in (* Callee fundec *) - let callee = (fd_e, c_e) in - record_call ctx.sideg callee caller - ); - S.combine_env (conv ctx) r fe f args fc es f_ask - - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end From 7ab1b0b7baca3b9ca30e88121b45f4d34e565620 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:16:54 +0300 Subject: [PATCH 378/566] Reorganize analysis lifters --- src/goblint_lib.ml | 15 +++++++++++---- src/{framework => lifters}/contextGasLifter.ml | 0 src/{framework => lifters}/contextGasLifter.mli | 0 src/{framework => lifters}/specLifters.ml | 0 src/{util => lifters}/wideningTokens.ml | 0 5 files changed, 11 insertions(+), 4 deletions(-) rename src/{framework => lifters}/contextGasLifter.ml (100%) rename src/{framework => lifters}/contextGasLifter.mli (100%) rename src/{framework => lifters}/specLifters.ml (100%) rename src/{util => lifters}/wideningTokens.ml (100%) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 192bf441f5..b8a0c6eedb 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -23,12 +23,10 @@ module CfgTools = CfgTools module Analyses = Analyses module ConstrSys = ConstrSys module Constraints = Constraints -module SpecLifters = SpecLifters module CompareConstraints = CompareConstraints module AnalysisState = AnalysisState module AnalysisStateUtil = AnalysisStateUtil module ControlSpecC = ControlSpecC -module ContextGasLifter = ContextGasLifter (** Master control program (MCP) is the analysis specification for the dynamic product of activated analyses. *) @@ -174,6 +172,17 @@ module AbortUnless = AbortUnless module PtranalAnalysis = PtranalAnalysis +(** {1 Analysis lifters} + + Transformations of analyses into extended analyses. *) + +module SpecLifters = SpecLifters +module ContextGasLifter = ContextGasLifter +module WideningTokens = WideningTokens + +module WitnessConstraints = WitnessConstraints + + (** {1 Domains} Domains used by analysis specifications and constraint systems are {{!Lattice.S} lattices}. @@ -329,7 +338,6 @@ module WitnessUtil = WitnessUtil Automaton-based GraphML witnesses used in SV-COMP. *) module MyARG = MyARG -module WitnessConstraints = WitnessConstraints module ArgTools = ArgTools module Witness = Witness module Graphml = Graphml @@ -340,7 +348,6 @@ module Graphml = Graphml module YamlWitness = YamlWitness module YamlWitnessType = YamlWitnessType -module WideningTokens = WideningTokens (** {3 Violation} diff --git a/src/framework/contextGasLifter.ml b/src/lifters/contextGasLifter.ml similarity index 100% rename from src/framework/contextGasLifter.ml rename to src/lifters/contextGasLifter.ml diff --git a/src/framework/contextGasLifter.mli b/src/lifters/contextGasLifter.mli similarity index 100% rename from src/framework/contextGasLifter.mli rename to src/lifters/contextGasLifter.mli diff --git a/src/framework/specLifters.ml b/src/lifters/specLifters.ml similarity index 100% rename from src/framework/specLifters.ml rename to src/lifters/specLifters.ml diff --git a/src/util/wideningTokens.ml b/src/lifters/wideningTokens.ml similarity index 100% rename from src/util/wideningTokens.ml rename to src/lifters/wideningTokens.ml From 01762ace12408323e501ef0ae7039a708f0c420e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:21:32 +0300 Subject: [PATCH 379/566] Rename SpecLifters -> RecursionTermLifter for split --- src/framework/control.ml | 2 +- src/goblint_lib.ml | 2 +- src/lifters/{specLifters.ml => recursionTermLifter.ml} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/lifters/{specLifters.ml => recursionTermLifter.ml} (100%) diff --git a/src/framework/control.ml b/src/framework/control.ml index dff398d00f..eebd50c208 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -9,7 +9,7 @@ open Analyses open ConstrSys open GobConfig open Constraints -open SpecLifters +open RecursionTermLifter module type S2S = Spec2Spec diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index b8a0c6eedb..04c2459723 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -176,7 +176,7 @@ module PtranalAnalysis = PtranalAnalysis Transformations of analyses into extended analyses. *) -module SpecLifters = SpecLifters +module RecursionTermLifter = RecursionTermLifter module ContextGasLifter = ContextGasLifter module WideningTokens = WideningTokens diff --git a/src/lifters/specLifters.ml b/src/lifters/recursionTermLifter.ml similarity index 100% rename from src/lifters/specLifters.ml rename to src/lifters/recursionTermLifter.ml From 0beb786d9fae26b0c7d0ff5d63c60a3307937ec8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:22:39 +0300 Subject: [PATCH 380/566] Remove non-recursion lifters from RecursionTermLifter --- src/lifters/recursionTermLifter.ml | 1025 ---------------------------- 1 file changed, 1025 deletions(-) diff --git a/src/lifters/recursionTermLifter.ml b/src/lifters/recursionTermLifter.ml index 2012b5cdcf..3de3810569 100644 --- a/src/lifters/recursionTermLifter.ml +++ b/src/lifters/recursionTermLifter.ml @@ -1,1030 +1,5 @@ -open Batteries open GoblintCil open Analyses -open GobConfig - - -(** Lifts a [Spec] so that the domain is [Hashcons]d *) -module HashconsLifter (S:Spec) - : Spec with module G = S.G - and module C = S.C -= -struct - module HConsedArg = - struct - (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) - (* see https://github.com/goblint/analyzer/issues/1005 *) - let assume_idempotent = GobConfig.get_string "ana.int.refinement" = "never" - end - module D = Lattice.HConsed (S.D) (HConsedArg) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt x = of_elt (D.unlift x) - end - - let name () = S.name () ^" hashconsed" - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init = S.init - let finalize = S.finalize - - let startstate v = D.lift (S.startstate v) - let exitstate v = D.lift (S.exitstate v) - let morphstate v d = D.lift (S.morphstate v (D.unlift d)) - - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) - } - - let context ctx fd = S.context (conv ctx) fd % D.unlift - let startcontext () = S.startcontext () - - let sync ctx reason = - D.lift @@ S.sync (conv ctx) reason - - let query ctx = - S.query (conv ctx) - - let assign ctx lv e = - D.lift @@ S.assign (conv ctx) lv e - - let vdecl ctx v = - D.lift @@ S.vdecl (conv ctx) v - - let branch ctx e tv = - D.lift @@ S.branch (conv ctx) e tv - - let body ctx f = - D.lift @@ S.body (conv ctx) f - - let return ctx r f = - D.lift @@ S.return (conv ctx) r f - - let asm ctx = - D.lift @@ S.asm (conv ctx) - - let skip ctx = - D.lift @@ S.skip (conv ctx) - - let enter ctx r f args = - List.map (fun (x,y) -> D.lift x, D.lift y) @@ S.enter (conv ctx) r f args - - let special ctx r f args = - D.lift @@ S.special (conv ctx) r f args - - let combine_env ctx r fe f args fc es f_ask = - D.lift @@ S.combine_env (conv ctx) r fe f args fc (D.unlift es) f_ask - - let combine_assign ctx r fe f args fc es f_ask = - D.lift @@ S.combine_assign (conv ctx) r fe f args fc (D.unlift es) f_ask - - let threadenter ctx ~multiple lval f args = - List.map D.lift @@ S.threadenter (conv ctx) ~multiple lval f args - - let threadspawn ctx ~multiple lval f args fctx = - D.lift @@ S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) - - let paths_as_set ctx = - List.map (fun x -> D.lift x) @@ S.paths_as_set (conv ctx) - - let event ctx e octx = - D.lift @@ S.event (conv ctx) e (conv octx) -end - -(** Lifts a [Spec] so that the context is [Hashcons]d. *) -module HashconsContextLifter (S:Spec) - : Spec with module D = S.D - and module G = S.G - and module C = Printable.HConsed (S.C) -= -struct - module D = S.D - module G = S.G - module C = Printable.HConsed (S.C) - module V = S.V - module P = S.P - - let name () = S.name () ^" context hashconsed" - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init = S.init - let finalize = S.finalize - - let startstate = S.startstate - let exitstate = S.exitstate - let morphstate = S.morphstate - - let conv ctx = - { ctx with context = (fun () -> C.unlift (ctx.context ())) } - - let context ctx fd = C.lift % S.context (conv ctx) fd - let startcontext () = C.lift @@ S.startcontext () - - let sync ctx reason = - S.sync (conv ctx) reason - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | Queries.IterPrevVars f -> - let g i (n, c, j) e = f i (n, Obj.repr (C.lift (Obj.obj c)), j) e in - S.query (conv ctx) (Queries.IterPrevVars g) - | _ -> S.query (conv ctx) q - - let assign ctx lv e = - S.assign (conv ctx) lv e - - let vdecl ctx v = - S.vdecl (conv ctx) v - - let branch ctx e tv = - S.branch (conv ctx) e tv - - let body ctx f = - S.body (conv ctx) f - - let return ctx r f = - S.return (conv ctx) r f - - let asm ctx = - S.asm (conv ctx) - - let skip ctx = - S.skip (conv ctx) - - let enter ctx r f args = - S.enter (conv ctx) r f args - - let special ctx r f args = - S.special (conv ctx) r f args - - let combine_env ctx r fe f args fc es f_ask = - S.combine_env (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - - let combine_assign ctx r fe f args fc es f_ask = - S.combine_assign (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - - let threadenter ctx ~multiple lval f args = - S.threadenter (conv ctx) ~multiple lval f args - - let threadspawn ctx ~multiple lval f args fctx = - S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) - - let paths_as_set ctx = S.paths_as_set (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - -(* see option ana.opt.equal *) -module OptEqual (S: Spec) = struct - module D = struct include S.D let equal x y = x == y || equal x y end - module G = struct include S.G let equal x y = x == y || equal x y end - module C = struct include S.C let equal x y = x == y || equal x y end - include (S : Spec with module D := D and module G := G and module C := C) -end - -(** If dbg.slice.on, stops entering functions after dbg.slice.n levels. *) -module LevelSliceLifter (S:Spec) - : Spec with module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) - and module G = S.G - and module C = S.C -= -struct - module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - let name () = S.name ()^" level sliced" - - let start_level = ref (`Top) - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init marshal = - if get_bool "dbg.slice.on" then - start_level := `Lifted (Int64.of_int (get_int "dbg.slice.n")); - S.init marshal - - let finalize = S.finalize - - let startstate v = (S.startstate v, !start_level) - let exitstate v = (S.exitstate v, !start_level) - let morphstate v (d,l) = (S.morphstate v d, l) - - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) - } - - let context ctx fd (d,_) = S.context (conv ctx) fd d - let startcontext () = S.startcontext () - - let lift_fun ctx f g h = - f @@ h (g (conv ctx)) - - let enter' ctx r f args = - let liftmap = List.map (fun (x,y) -> (x, snd ctx.local), (y, snd ctx.local)) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) - - let lift ctx d = (d, snd ctx.local) - let lift_start_level d = (d, !start_level) - - let sync ctx reason = lift_fun ctx (lift ctx) S.sync ((|>) reason) - let query' ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun x -> x q) - let assign ctx lv e = lift_fun ctx (lift ctx) S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx (lift ctx) S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx (lift ctx) S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx (lift ctx) S.body ((|>) f) - let return ctx r f = lift_fun ctx (lift ctx) S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx (lift ctx) S.asm identity - let skip ctx = lift_fun ctx (lift ctx) S.skip identity - let special ctx r f args = lift_fun ctx (lift ctx) S.special ((|>) args % (|>) f % (|>) r) - let combine_env' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (lift ctx) (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - - let leq0 = function - | `Top -> false - | `Lifted x -> x <= 0L - | `Bot -> true - - let sub1 = function - | `Lifted x -> `Lifted (Int64.sub x 1L) - | x -> x - - let add1 = function - | `Lifted x -> `Lifted (Int64.add x 1L) - | x -> x - - let paths_as_set ctx = - let liftmap = List.map (fun x -> (x, snd ctx.local)) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) - - let event ctx e octx = - lift_fun ctx (lift ctx) S.event ((|>) (conv octx) % (|>) e) - - let enter ctx r f args = - let (d,l) = ctx.local in - if leq0 l then - [ctx.local, D.bot ()] - else - enter' {ctx with local=(d, sub1 l)} r f args - - let combine_env ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in - let l = add1 l in - if leq0 l then - (d, l) - else - let d',_ = combine_env' ctx r fe f args fc es f_ask in - (d', l) - - let combine_assign ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in - (* No need to add1 here, already done in combine_env. *) - if leq0 l then - (d, l) - else - let d',_ = combine_assign' ctx r fe f args fc es f_ask in - (d', l) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | Queries.EvalFunvar e -> - let (d,l) = ctx.local in - if leq0 l then - Queries.AD.empty () - else - query' ctx (Queries.EvalFunvar e) - | q -> query' ctx q -end - - -(** Limits the number of widenings per node. *) -module LimitLifter (S:Spec) = -struct - include (S : module type of S with module D := S.D and type marshal = S.marshal) - - let name () = S.name ()^" limited" - - let limit = ref 0 - - let init marshal = - limit := get_int "dbg.limit.widen"; - S.init marshal - - module H = MyCFG.NodeH - let h = H.create 13 - let incr k = - H.modify_def 1 k (fun v -> - if v >= !limit then failwith (GobPretty.sprintf "LimitLifter: Reached limit (%d) for node %a" !limit Node.pretty_plain_short (Option.get !MyCFG.current_node)); - v+1 - ) h; - module D = struct - include S.D - let widen x y = Option.may incr !MyCFG.current_node; widen x y (* when is this None? *) - end -end - - -(* widening on contexts, keeps contexts for calls only in D *) -module WidenContextLifterSide (S:Spec) -= -struct - module DD = - struct - include S.D - let printXml f d = BatPrintf.fprintf f "%a" printXml d - end - module M = MapDomain.MapBot (Basetype.Variables) (DD) (* should be CilFun -> S.C, but CilFun is not Groupable, and S.C is no Lattice *) - - module D = struct - include Lattice.Prod (S.D) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.D.printXml d M.printXml m - end - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - - let name () = S.name ()^" with widened contexts" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - let inj f x = f x, M.bot () - - let startcontext () = S.startcontext () - let startstate = inj S.startstate - let exitstate = inj S.exitstate - let morphstate v (d,m) = S.morphstate v d, m - - - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) - } - - let context ctx fd (d,m) = S.context (conv ctx) fd d (* just the child analysis' context *) - - let lift_fun ctx f g = g (f (conv ctx)), snd ctx.local - - let sync ctx reason = lift_fun ctx S.sync ((|>) reason) - let query ctx = S.query (conv ctx) - let assign ctx lv e = lift_fun ctx S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx S.body ((|>) f) - let return ctx r f = lift_fun ctx S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx S.asm identity - let skip ctx = lift_fun ctx S.skip identity - let special ctx r f args = lift_fun ctx S.special ((|>) args % (|>) f % (|>) r) - - let event ctx e octx = lift_fun ctx S.event ((|>) (conv octx) % (|>) e) - - let threadenter ctx ~multiple lval f args = S.threadenter (conv ctx) ~multiple lval f args |> List.map (fun d -> (d, snd ctx.local)) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - - let enter ctx r f args = - let m = snd ctx.local in - let d' v_cur = - if ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.context.widen" ~keepAttr:"widen" ~removeAttr:"no-widen" f then ( - let v_old = M.find f.svar m in (* S.D.bot () if not found *) - let v_new = S.D.widen v_old (S.D.join v_old v_cur) in - Messages.(if tracing && not (S.D.equal v_old v_new) then tracel "widen-context" "enter results in new context for function %s" f.svar.vname); - v_new, M.add f.svar v_new m - ) - else - v_cur, m - in - S.enter (conv ctx) r f args - |> List.map (fun (c,v) -> (c,m), d' v) (* c: caller, v: callee *) - - let paths_as_set ctx = - let m = snd ctx.local in - S.paths_as_set (conv ctx) |> List.map (fun v -> (v,m)) - - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) -end - - -(** Lifts a [Spec] with a special bottom element that represent unreachable code. *) -module DeadCodeLifter (S:Spec) - : Spec with module D = Dom (S.D) - and module G = S.G - and module C = S.C -= -struct - module D = Dom (S.D) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include Printable.Option (S.P) (struct let name = "None" end) - - let of_elt = function - | `Lifted x -> Some (S.P.of_elt x) - | _ -> None - end - - let name () = S.name ()^" lifted" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - - let startcontext () = S.startcontext () - let startstate v = `Lifted (S.startstate v) - let exitstate v = `Lifted (S.exitstate v) - let morphstate v d = try `Lifted (S.morphstate v (D.unlift d)) with Deadcode -> d - - - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) - } - - let context ctx fd = S.context (conv ctx) fd % D.unlift - - let lift_fun ctx f g h b = - try f @@ h (g (conv ctx)) - with Deadcode -> b - - let sync ctx reason = lift_fun ctx D.lift S.sync ((|>) reason) `Bot - - let enter ctx r f args = - let liftmap = List.map (fun (x,y) -> D.lift x, D.lift y) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) [] - - let paths_as_set ctx = - let liftmap = List.map (fun x -> D.lift x) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) [D.bot ()] (* One dead path instead of none, such that combine_env gets called for functions with dead normal return (and thus longjmpy returns can be correctly handled by lifter). *) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun (x) -> x q) (Queries.Result.bot q) - let assign ctx lv e = lift_fun ctx D.lift S.assign ((|>) e % (|>) lv) `Bot - let vdecl ctx v = lift_fun ctx D.lift S.vdecl ((|>) v) `Bot - let branch ctx e tv = lift_fun ctx D.lift S.branch ((|>) tv % (|>) e) `Bot - let body ctx f = lift_fun ctx D.lift S.body ((|>) f) `Bot - let return ctx r f = lift_fun ctx D.lift S.return ((|>) f % (|>) r) `Bot - let asm ctx = lift_fun ctx D.lift S.asm identity `Bot - let skip ctx = lift_fun ctx D.lift S.skip identity `Bot - let special ctx r f args = lift_fun ctx D.lift S.special ((|>) args % (|>) f % (|>) r) `Bot - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx D.lift (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot - - let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot -end - - -(** Add path sensitivity to a analysis *) -module PathSensitive2 (Spec:Spec) - : Spec - with module G = Spec.G - and module C = Spec.C - and module V = Spec.V -= -struct - module D = - struct - (* TODO is it really worth it to check every time instead of just using sets and joining later? *) - module R = - struct - include Spec.P - type elt = Spec.D.t - end - module J = SetDomain.Joined (Spec.D) - include DisjointDomain.ProjectiveSet (Spec.D) (J) (R) - let name () = "PathSensitive (" ^ name () ^ ")" - - let printXml f x = - let print_one x = - BatPrintf.fprintf f "\n%a" Spec.D.printXml x - in - iter print_one x - end - - module G = Spec.G - module C = Spec.C - module V = Spec.V - module P = UnitP - - let name () = "PathSensitive2("^Spec.name ()^")" - - type marshal = Spec.marshal - let init = Spec.init - let finalize = Spec.finalize - - let startcontext () = Spec.startcontext () - let exitstate v = D.singleton (Spec.exitstate v) - let startstate v = D.singleton (Spec.startstate v) - let morphstate v d = D.map (Spec.morphstate v) d - - let conv ctx x = - let rec ctx' = { ctx with ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx' q) - ; local = x - ; split = (ctx.split % D.singleton) } - in - ctx' - - let context ctx fd l = - if D.cardinal l <> 1 then - failwith "PathSensitive2.context must be called with a singleton set." - else - let x = D.choose l in - Spec.context (conv ctx x) fd x - - - let map ctx f g = - let h x xs = - try D.add (g (f (conv ctx x))) xs - with Deadcode -> xs - in - let d = D.fold h ctx.local (D.empty ()) in - if D.is_bot d then raise Deadcode else d - - let fold' ctx f g h a = - let k x a = - try h a @@ g @@ f @@ conv ctx x - with Deadcode -> a - in - D.fold k ctx.local a - - let assign ctx l e = map ctx Spec.assign (fun h -> h l e ) - let vdecl ctx v = map ctx Spec.vdecl (fun h -> h v) - let body ctx f = map ctx Spec.body (fun h -> h f ) - let return ctx e f = map ctx Spec.return (fun h -> h e f ) - let branch ctx e tv = map ctx Spec.branch (fun h -> h e tv) - let asm ctx = map ctx Spec.asm identity - let skip ctx = map ctx Spec.skip identity - let special ctx l f a = map ctx Spec.special (fun h -> h l f a) - - let event ctx e octx = - let fd1 = D.choose octx.local in - map ctx Spec.event (fun h -> h e (conv octx fd1)) - - let threadenter ctx ~multiple lval f args = - let g xs ys = (List.map (fun y -> D.singleton y) ys) @ xs in - fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] - - let threadspawn ctx ~multiple lval f args fctx = - let fd1 = D.choose fctx.local in - map ctx (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fctx fd1)) - - let sync ctx reason = map ctx Spec.sync (fun h -> h reason) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - (* TODO: handle Invariant path like PathSensitive3? *) - (* join results so that they are sound for all paths *) - let module Result = (val Queries.Result.lattice q) in - fold' ctx Spec.query identity (fun x f -> Result.join x (f q)) (Result.bot ()) - - let enter ctx l f a = - let g xs ys = (List.map (fun (x,y) -> D.singleton x, D.singleton y) ys) @ xs in - fold' ctx Spec.enter (fun h -> h l f a) g [] - - let paths_as_set ctx = - (* Path-sensitivity is only here, not below! *) - let elems = D.elements ctx.local in - List.map (D.singleton) elems - - let combine_env ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in - let k x y = - if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; - try - let r = Spec.combine_env (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; - D.add r y - with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead"; - y - in - let d = D.fold k d (D.bot ()) in - if D.is_bot d then raise Deadcode else d - - let combine_assign ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in - let k x y = - if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; - try - let r = Spec.combine_assign (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; - D.add r y - with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead"; - y - in - let d = D.fold k d (D.bot ()) in - if D.is_bot d then raise Deadcode else d -end - -module DeadBranchLifter (S: Spec): Spec = -struct - include S - - let name () = "DeadBranch (" ^ S.name () ^ ")" - - (* Two global invariants: - 1. S.V -> S.G -- used for S - 2. node -> (exp -> flat bool) -- used for warnings *) - - module V = - struct - include Printable.EitherConf (struct let expand1 = false let expand2 = true end) (S.V) (Node) - let name () = "DeadBranch" - let s x = `Left x - let node x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | `Right _ -> true - end - - module EM = - struct - include MapDomain.MapBot (Basetype.CilExp) (BoolDomain.FlatBool) - let name () = "branches" - end - - module G = - struct - include Lattice.Lift2 (S.G) (EM) - let name () = "deadbranch" - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "DeadBranchLifter.s" - let node = function - | `Bot -> EM.bot () - | `Lifted2 x -> x - | _ -> failwith "DeadBranchLifter.node" - let create_s s = `Lifted1 s - let create_node node = `Lifted2 node - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> - let em = G.node (ctx.global (V.node g)) in - EM.iter (fun exp tv -> - match tv with - | `Lifted tv -> - let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) - let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in - M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv - | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) - M.msg_final Error ~category:Analyzer ~tags:[Category Unsound] "Both branches dead"; - M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp - | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) - | `Top -> (* may be both true and false *) - () - ) em; - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - - (* node vars for dead branches *) - begin match vq with - | Node {node; _} -> - vf (Obj.repr (V.node node)) - | _ -> - () - end - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - let context ctx = S.context (conv ctx) - - let branch ctx exp tv = - if !AnalysisState.postsolving then ( - try - let r = branch ctx exp tv in - (* branch is live *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) - r - with Deadcode -> - (* branch is dead *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) - raise Deadcode - ) - else ( - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) - branch ctx exp tv - ) - - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - -module LongjmpLifter (S: Spec): Spec = -struct - include S - - let name () = "Longjmp (" ^ S.name () ^ ")" - - module V = - struct - include Printable.Either3Conf (struct let expand1 = false let expand2 = true let expand3 = true end) (S.V) (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C)) - let name () = "longjmp" - let s x = `Left x - let longjmpto x = `Middle x - let longjmpret x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | _ -> false - end - - module G = - struct - include Lattice.Lift2 (S.G) (S.D) - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "LongjmpLifter.s" - let local = function - | `Bot -> S.D.bot () - | `Lifted2 x -> x - | _ -> failwith "LongjmpLifter.local" - let create_s s = `Lifted1 s - let create_local local = `Lifted2 local - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" S.D.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | _ -> - Queries.Result.top q - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | _ -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - (* TODO: vars? *) - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let context ctx = S.context (conv ctx) - - let combine_env ctx lv e f args fc fd f_ask = - let conv_ctx = conv ctx in - let current_fundec = Node.find_fundec ctx.node in - let handle_longjmp (cd, fc, longfd) = - (* This is called per-path. *) - let rec cd_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); - local = cd; - } - in - let longfd_ctx = - (* Inner scope to prevent unsynced longfd_ctx from being used. *) - (* Extra sync like with normal combine. *) - let rec sync_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query sync_ctx q); - local = longfd; - prev_node = Function f; - } - in - let synced = S.sync sync_ctx `Join in - let rec longfd_ctx = - { sync_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query longfd_ctx q); - local = synced; - } - in - longfd_ctx - in - let combined = lazy ( (* does not depend on target, do at most once *) - (* Globals are non-problematic here, as they are always carried around without any issues! *) - (* A combine call is mostly needed to ensure locals have appropriate values. *) - (* Using f from called function on purpose here! Needed? *) - S.combine_env cd_ctx None e f args fc longfd_ctx.local (Analyses.ask_of_ctx longfd_ctx) (* no lval because longjmp return skips return value assignment *) - ) - in - let returned = lazy ( (* does not depend on target, do at most once *) - let rec combined_ctx = - { cd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query combined_ctx q); - local = Lazy.force combined; - } - in - S.return combined_ctx None current_fundec - ) - in - let (active_targets, _) = longfd_ctx.ask ActiveJumpBuf in - let valid_targets = cd_ctx.ask ValidLongJmp in - let handle_target target = match target with - | JmpBufDomain.BufferEntryOrTop.AllTargets -> () (* The warning is already emitted at the point where the longjmp happens *) - | Target (target_node, target_context) -> - let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Fun: Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force combined)) - (* No need to propagate this outwards here, the set of valid longjumps is part of the context, we can never have the same context setting the longjmp multiple times *) - ) - (* Appropriate setjmp is not in current function & current context *) - else if JmpBufDomain.JmpBufSet.mem target valid_targets then - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) - else - (* It actually is not handled here but was propagated here spuriously, we already warned at the location where this issue is caused *) - (* As the validlongjumps inside the callee is a a superset of the ones inside the caller *) - () - in - JmpBufDomain.JmpBufSet.iter handle_target active_targets - in - if M.tracing then M.tracel "longjmp" "longfd getg %a" CilType.Fundec.pretty f; - let longfd = G.local (ctx.global (V.longjmpret (f, Option.get fc))) in - if M.tracing then M.tracel "longjmp" "longfd %a" D.pretty longfd; - if not (D.is_bot longfd) then - handle_longjmp (ctx.local, fc, longfd); - S.combine_env (conv_ctx) lv e f args fc fd f_ask - - let combine_assign ctx lv e f args fc fd f_ask = - S.combine_assign (conv ctx) lv e f args fc fd f_ask - - let special ctx lv f args = - let conv_ctx = conv ctx in - match (LibraryFunctions.find f).special args with - | Setjmp {env} -> - (* Handling of returning for the first time *) - let normal_return = S.special conv_ctx lv f args in - let jmp_return = G.local (ctx.global (V.longjmpto (ctx.prev_node, ctx.context ()))) in - if S.D.is_bot jmp_return then - normal_return - else ( - let rec jmp_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query jmp_ctx q); - local = jmp_return; - } - in - let longjmped = S.event jmp_ctx (Events.Longjmped {lval=lv}) jmp_ctx in - S.D.join normal_return longjmped - ) - | Longjmp {env; value} -> - let current_fundec = Node.find_fundec ctx.node in - let handle_path path = ( - let rec path_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query path_ctx q); - local = path; - } - in - let specialed = lazy ( (* does not depend on target, do at most once *) - S.special path_ctx lv f args - ) - in - let returned = lazy ( (* does not depend on target, do at most once *) - let rec specialed_ctx = - { path_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query specialed_ctx q); - local = Lazy.force specialed; - } - in - S.return specialed_ctx None current_fundec - ) - in - (* Eval `env` again to avoid having to construct bespoke ctx to ask *) - let targets = path_ctx.ask (EvalJumpBuf env) in - let valid_targets = path_ctx.ask ValidLongJmp in - if M.tracing then Messages.tracel "longjmp" "Jumping to %a" JmpBufDomain.JmpBufSet.pretty targets; - let handle_target target = match target with - | JmpBufDomain.BufferEntryOrTop.AllTargets -> - M.warn ~category:Imprecise "Longjmp to potentially invalid target, as contents of buffer %a may be unknown! (imprecision due to heap?)" d_exp env; - M.msg_final Error ~category:Unsound ~tags:[Category Imprecise; Category Call] "Longjmp to unknown target ignored" - | Target (target_node, target_context) -> - let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force specialed)) - ) - else if JmpBufDomain.JmpBufSet.mem target valid_targets then ( - if M.tracing then Messages.tracel "longjmp" "Longjmp to somewhere else, side-effect to %i" (S.C.hash (ctx.context ())); - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) - ) - else - M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target! (Target %a in Function %a which may have already returned or is in a different thread)" Node.pretty target_node CilType.Fundec.pretty target_fundec - in - if JmpBufDomain.JmpBufSet.is_empty targets then - M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target (%a is bot?!)" d_exp env - else - JmpBufDomain.JmpBufSet.iter handle_target targets - ) - in - List.iter handle_path (S.paths_as_set conv_ctx); - if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( - AnalysisState.svcomp_may_not_terminate := true; - M.warn ~category:Termination "The program might not terminate! (Longjmp)" - ); - S.D.bot () - | _ -> S.special conv_ctx lv f args - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end (** Add cycle detection in the context-sensitive dynamic function call graph to an analysis *) From cb405d752a45f80cc610552805e7014117ee37dc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:23:11 +0300 Subject: [PATCH 381/566] Remove RecursionTermLifter from SpecLifters --- src/lifters/specLifters.ml | 144 ------------------------------------- 1 file changed, 144 deletions(-) diff --git a/src/lifters/specLifters.ml b/src/lifters/specLifters.ml index 2012b5cdcf..1be137cf8c 100644 --- a/src/lifters/specLifters.ml +++ b/src/lifters/specLifters.ml @@ -1025,147 +1025,3 @@ struct let asm ctx = S.asm (conv ctx) let event ctx e octx = S.event (conv ctx) e (conv octx) end - - -(** Add cycle detection in the context-sensitive dynamic function call graph to an analysis *) -module RecursionTermLifter (S: Spec) - : Spec with module D = S.D - and module C = S.C -= -(* two global invariants: - - S.V -> S.G - Needed to store the previously built global invariants - - fundec * S.C -> (Set (fundec * S.C)) - The second global invariant maps from the callee fundec and context to a set of caller fundecs and contexts. - This structure therefore stores the context-sensitive call graph. - For example: - let the function f in context c call function g in context c'. - In the global invariant structure it would be stored like this: (g,c') -> {(f, c)} -*) - -struct - include S - - (* contains all the callee fundecs and contexts *) - module V = GVarFC(S.V)(S.C) - - (* Tuple containing the fundec and context of a caller *) - module Call = Printable.Prod (CilType.Fundec) (S.C) - - (* Set containing multiple caller tuples *) - module CallerSet = SetDomain.Make (Call) - - module G = - struct - include Lattice.Lift2 (G) (CallerSet) - - let spec = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "RecursionTermLifter.spec" - - let callers = function - | `Bot -> CallerSet.bot () - | `Lifted2 x -> x - | _ -> failwith "RecursionTermLifter.callGraph" - - let create_spec spec = `Lifted1 spec - let create_singleton_caller caller = `Lifted2 (CallerSet.singleton caller) - - let printXml f = function - | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CallerSet.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - - end - - let name () = "RecursionTermLifter (" ^ S.name () ^ ")" - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.spec (ctx.global (V.spec v))); - sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_spec g)); - } - - let cycleDetection ctx call = - let module LH = Hashtbl.Make (Printable.Prod (CilType.Fundec) (S.C)) in - let module LS = Set.Make (Printable.Prod (CilType.Fundec) (S.C)) in - (* find all cycles/SCCs *) - let global_visited_calls = LH.create 100 in - - (* DFS *) - let rec iter_call (path_visited_calls: LS.t) ((fundec, _) as call) = - if LS.mem call path_visited_calls then ( - AnalysisState.svcomp_may_not_terminate := true; (*set the indicator for a non-terminating program for the sv comp*) - (*Cycle found*) - let loc = M.Location.CilLocation fundec.svar.vdecl in - M.warn ~loc ~category:Termination "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec) (* output a warning for non-termination*) - else if not (LH.mem global_visited_calls call) then begin - LH.replace global_visited_calls call (); - let new_path_visited_calls = LS.add call path_visited_calls in - let gvar = V.call call in - let callers = G.callers (ctx.global gvar) in - CallerSet.iter (fun to_call -> - iter_call new_path_visited_calls to_call - ) callers; - end - in - iter_call LS.empty call - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal v -> - (* check result of loop analysis *) - if not (ctx.ask Queries.MustTermAllLoops) then - AnalysisState.svcomp_may_not_terminate := true; - let v: V.t = Obj.obj v in - begin match v with - | `Left v' -> - S.query (conv ctx) (WarnGlobal (Obj.repr v')) - | `Right call -> cycleDetection ctx call (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) - end - | InvariantGlobal v -> - let v: V.t = Obj.obj v in - begin match v with - | `Left v -> - S.query (conv ctx) (InvariantGlobal (Obj.repr v)) - | `Right v -> - Queries.Result.top q - end - | _ -> S.query (conv ctx) q - - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - - - let record_call sideg callee caller = - sideg (V.call callee) (G.create_singleton_caller caller) - - let enter ctx = S.enter (conv ctx) - let context ctx = S.context (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx r fe f args fc es f_ask = - if !AnalysisState.postsolving then ( - let c_r: S.C.t = ctx.context () in (* Caller context *) - let nodeF = ctx.node in - let fd_r : fundec = Node.find_fundec nodeF in (* Caller fundec *) - let caller: (fundec * S.C.t) = (fd_r, c_r) in - let c_e: S.C.t = Option.get fc in (* Callee context *) - let fd_e : fundec = f in (* Callee fundec *) - let callee = (fd_e, c_e) in - record_call ctx.sideg callee caller - ); - S.combine_env (conv ctx) r fe f args fc es f_ask - - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end From 8edddc3904298245f9440e2b9a731d29c16d8cdc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:26:09 +0300 Subject: [PATCH 382/566] Rename SpecLifters -> LongjmpLifter for split --- src/framework/control.ml | 2 +- src/goblint_lib.ml | 2 +- src/lifters/{specLifters.ml => longjmpLifter.ml} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/lifters/{specLifters.ml => longjmpLifter.ml} (100%) diff --git a/src/framework/control.ml b/src/framework/control.ml index 56f42e78d2..947be9e6b0 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -9,7 +9,7 @@ open Analyses open ConstrSys open GobConfig open Constraints -open SpecLifters +open LongjmpLifter module type S2S = Spec2Spec diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index a1b03fe618..b8d0638bbb 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -176,7 +176,7 @@ module PtranalAnalysis = PtranalAnalysis Transformations of analyses into extended analyses. *) -module SpecLifters = SpecLifters +module LongjmpLifter = LongjmpLifter module RecursionTermLifter = RecursionTermLifter module ContextGasLifter = ContextGasLifter module WideningTokens = WideningTokens diff --git a/src/lifters/specLifters.ml b/src/lifters/longjmpLifter.ml similarity index 100% rename from src/lifters/specLifters.ml rename to src/lifters/longjmpLifter.ml From 259ea677f5a2fbcca481e24de836da03ca39f61e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:26:53 +0300 Subject: [PATCH 383/566] Remove non-longjmp lifters from LongjmpLifter --- src/lifters/longjmpLifter.ml | 780 ----------------------------------- 1 file changed, 780 deletions(-) diff --git a/src/lifters/longjmpLifter.ml b/src/lifters/longjmpLifter.ml index 1be137cf8c..1150bc1bca 100644 --- a/src/lifters/longjmpLifter.ml +++ b/src/lifters/longjmpLifter.ml @@ -1,788 +1,8 @@ -open Batteries open GoblintCil open Analyses open GobConfig -(** Lifts a [Spec] so that the domain is [Hashcons]d *) -module HashconsLifter (S:Spec) - : Spec with module G = S.G - and module C = S.C -= -struct - module HConsedArg = - struct - (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) - (* see https://github.com/goblint/analyzer/issues/1005 *) - let assume_idempotent = GobConfig.get_string "ana.int.refinement" = "never" - end - module D = Lattice.HConsed (S.D) (HConsedArg) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt x = of_elt (D.unlift x) - end - - let name () = S.name () ^" hashconsed" - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init = S.init - let finalize = S.finalize - - let startstate v = D.lift (S.startstate v) - let exitstate v = D.lift (S.exitstate v) - let morphstate v d = D.lift (S.morphstate v (D.unlift d)) - - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) - } - - let context ctx fd = S.context (conv ctx) fd % D.unlift - let startcontext () = S.startcontext () - - let sync ctx reason = - D.lift @@ S.sync (conv ctx) reason - - let query ctx = - S.query (conv ctx) - - let assign ctx lv e = - D.lift @@ S.assign (conv ctx) lv e - - let vdecl ctx v = - D.lift @@ S.vdecl (conv ctx) v - - let branch ctx e tv = - D.lift @@ S.branch (conv ctx) e tv - - let body ctx f = - D.lift @@ S.body (conv ctx) f - - let return ctx r f = - D.lift @@ S.return (conv ctx) r f - - let asm ctx = - D.lift @@ S.asm (conv ctx) - - let skip ctx = - D.lift @@ S.skip (conv ctx) - - let enter ctx r f args = - List.map (fun (x,y) -> D.lift x, D.lift y) @@ S.enter (conv ctx) r f args - - let special ctx r f args = - D.lift @@ S.special (conv ctx) r f args - - let combine_env ctx r fe f args fc es f_ask = - D.lift @@ S.combine_env (conv ctx) r fe f args fc (D.unlift es) f_ask - - let combine_assign ctx r fe f args fc es f_ask = - D.lift @@ S.combine_assign (conv ctx) r fe f args fc (D.unlift es) f_ask - - let threadenter ctx ~multiple lval f args = - List.map D.lift @@ S.threadenter (conv ctx) ~multiple lval f args - - let threadspawn ctx ~multiple lval f args fctx = - D.lift @@ S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) - - let paths_as_set ctx = - List.map (fun x -> D.lift x) @@ S.paths_as_set (conv ctx) - - let event ctx e octx = - D.lift @@ S.event (conv ctx) e (conv octx) -end - -(** Lifts a [Spec] so that the context is [Hashcons]d. *) -module HashconsContextLifter (S:Spec) - : Spec with module D = S.D - and module G = S.G - and module C = Printable.HConsed (S.C) -= -struct - module D = S.D - module G = S.G - module C = Printable.HConsed (S.C) - module V = S.V - module P = S.P - - let name () = S.name () ^" context hashconsed" - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init = S.init - let finalize = S.finalize - - let startstate = S.startstate - let exitstate = S.exitstate - let morphstate = S.morphstate - - let conv ctx = - { ctx with context = (fun () -> C.unlift (ctx.context ())) } - - let context ctx fd = C.lift % S.context (conv ctx) fd - let startcontext () = C.lift @@ S.startcontext () - - let sync ctx reason = - S.sync (conv ctx) reason - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | Queries.IterPrevVars f -> - let g i (n, c, j) e = f i (n, Obj.repr (C.lift (Obj.obj c)), j) e in - S.query (conv ctx) (Queries.IterPrevVars g) - | _ -> S.query (conv ctx) q - - let assign ctx lv e = - S.assign (conv ctx) lv e - - let vdecl ctx v = - S.vdecl (conv ctx) v - - let branch ctx e tv = - S.branch (conv ctx) e tv - - let body ctx f = - S.body (conv ctx) f - - let return ctx r f = - S.return (conv ctx) r f - - let asm ctx = - S.asm (conv ctx) - - let skip ctx = - S.skip (conv ctx) - - let enter ctx r f args = - S.enter (conv ctx) r f args - - let special ctx r f args = - S.special (conv ctx) r f args - - let combine_env ctx r fe f args fc es f_ask = - S.combine_env (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - - let combine_assign ctx r fe f args fc es f_ask = - S.combine_assign (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - - let threadenter ctx ~multiple lval f args = - S.threadenter (conv ctx) ~multiple lval f args - - let threadspawn ctx ~multiple lval f args fctx = - S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) - - let paths_as_set ctx = S.paths_as_set (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - -(* see option ana.opt.equal *) -module OptEqual (S: Spec) = struct - module D = struct include S.D let equal x y = x == y || equal x y end - module G = struct include S.G let equal x y = x == y || equal x y end - module C = struct include S.C let equal x y = x == y || equal x y end - include (S : Spec with module D := D and module G := G and module C := C) -end - -(** If dbg.slice.on, stops entering functions after dbg.slice.n levels. *) -module LevelSliceLifter (S:Spec) - : Spec with module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) - and module G = S.G - and module C = S.C -= -struct - module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - let name () = S.name ()^" level sliced" - - let start_level = ref (`Top) - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init marshal = - if get_bool "dbg.slice.on" then - start_level := `Lifted (Int64.of_int (get_int "dbg.slice.n")); - S.init marshal - - let finalize = S.finalize - - let startstate v = (S.startstate v, !start_level) - let exitstate v = (S.exitstate v, !start_level) - let morphstate v (d,l) = (S.morphstate v d, l) - - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) - } - - let context ctx fd (d,_) = S.context (conv ctx) fd d - let startcontext () = S.startcontext () - - let lift_fun ctx f g h = - f @@ h (g (conv ctx)) - - let enter' ctx r f args = - let liftmap = List.map (fun (x,y) -> (x, snd ctx.local), (y, snd ctx.local)) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) - - let lift ctx d = (d, snd ctx.local) - let lift_start_level d = (d, !start_level) - - let sync ctx reason = lift_fun ctx (lift ctx) S.sync ((|>) reason) - let query' ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun x -> x q) - let assign ctx lv e = lift_fun ctx (lift ctx) S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx (lift ctx) S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx (lift ctx) S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx (lift ctx) S.body ((|>) f) - let return ctx r f = lift_fun ctx (lift ctx) S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx (lift ctx) S.asm identity - let skip ctx = lift_fun ctx (lift ctx) S.skip identity - let special ctx r f args = lift_fun ctx (lift ctx) S.special ((|>) args % (|>) f % (|>) r) - let combine_env' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (lift ctx) (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - - let leq0 = function - | `Top -> false - | `Lifted x -> x <= 0L - | `Bot -> true - - let sub1 = function - | `Lifted x -> `Lifted (Int64.sub x 1L) - | x -> x - - let add1 = function - | `Lifted x -> `Lifted (Int64.add x 1L) - | x -> x - - let paths_as_set ctx = - let liftmap = List.map (fun x -> (x, snd ctx.local)) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) - - let event ctx e octx = - lift_fun ctx (lift ctx) S.event ((|>) (conv octx) % (|>) e) - - let enter ctx r f args = - let (d,l) = ctx.local in - if leq0 l then - [ctx.local, D.bot ()] - else - enter' {ctx with local=(d, sub1 l)} r f args - - let combine_env ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in - let l = add1 l in - if leq0 l then - (d, l) - else - let d',_ = combine_env' ctx r fe f args fc es f_ask in - (d', l) - - let combine_assign ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in - (* No need to add1 here, already done in combine_env. *) - if leq0 l then - (d, l) - else - let d',_ = combine_assign' ctx r fe f args fc es f_ask in - (d', l) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | Queries.EvalFunvar e -> - let (d,l) = ctx.local in - if leq0 l then - Queries.AD.empty () - else - query' ctx (Queries.EvalFunvar e) - | q -> query' ctx q -end - - -(** Limits the number of widenings per node. *) -module LimitLifter (S:Spec) = -struct - include (S : module type of S with module D := S.D and type marshal = S.marshal) - - let name () = S.name ()^" limited" - - let limit = ref 0 - - let init marshal = - limit := get_int "dbg.limit.widen"; - S.init marshal - - module H = MyCFG.NodeH - let h = H.create 13 - let incr k = - H.modify_def 1 k (fun v -> - if v >= !limit then failwith (GobPretty.sprintf "LimitLifter: Reached limit (%d) for node %a" !limit Node.pretty_plain_short (Option.get !MyCFG.current_node)); - v+1 - ) h; - module D = struct - include S.D - let widen x y = Option.may incr !MyCFG.current_node; widen x y (* when is this None? *) - end -end - - -(* widening on contexts, keeps contexts for calls only in D *) -module WidenContextLifterSide (S:Spec) -= -struct - module DD = - struct - include S.D - let printXml f d = BatPrintf.fprintf f "%a" printXml d - end - module M = MapDomain.MapBot (Basetype.Variables) (DD) (* should be CilFun -> S.C, but CilFun is not Groupable, and S.C is no Lattice *) - - module D = struct - include Lattice.Prod (S.D) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.D.printXml d M.printXml m - end - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - - let name () = S.name ()^" with widened contexts" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - let inj f x = f x, M.bot () - - let startcontext () = S.startcontext () - let startstate = inj S.startstate - let exitstate = inj S.exitstate - let morphstate v (d,m) = S.morphstate v d, m - - - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) - } - - let context ctx fd (d,m) = S.context (conv ctx) fd d (* just the child analysis' context *) - - let lift_fun ctx f g = g (f (conv ctx)), snd ctx.local - - let sync ctx reason = lift_fun ctx S.sync ((|>) reason) - let query ctx = S.query (conv ctx) - let assign ctx lv e = lift_fun ctx S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx S.body ((|>) f) - let return ctx r f = lift_fun ctx S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx S.asm identity - let skip ctx = lift_fun ctx S.skip identity - let special ctx r f args = lift_fun ctx S.special ((|>) args % (|>) f % (|>) r) - - let event ctx e octx = lift_fun ctx S.event ((|>) (conv octx) % (|>) e) - - let threadenter ctx ~multiple lval f args = S.threadenter (conv ctx) ~multiple lval f args |> List.map (fun d -> (d, snd ctx.local)) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - - let enter ctx r f args = - let m = snd ctx.local in - let d' v_cur = - if ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.context.widen" ~keepAttr:"widen" ~removeAttr:"no-widen" f then ( - let v_old = M.find f.svar m in (* S.D.bot () if not found *) - let v_new = S.D.widen v_old (S.D.join v_old v_cur) in - Messages.(if tracing && not (S.D.equal v_old v_new) then tracel "widen-context" "enter results in new context for function %s" f.svar.vname); - v_new, M.add f.svar v_new m - ) - else - v_cur, m - in - S.enter (conv ctx) r f args - |> List.map (fun (c,v) -> (c,m), d' v) (* c: caller, v: callee *) - - let paths_as_set ctx = - let m = snd ctx.local in - S.paths_as_set (conv ctx) |> List.map (fun v -> (v,m)) - - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) -end - - -(** Lifts a [Spec] with a special bottom element that represent unreachable code. *) -module DeadCodeLifter (S:Spec) - : Spec with module D = Dom (S.D) - and module G = S.G - and module C = S.C -= -struct - module D = Dom (S.D) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include Printable.Option (S.P) (struct let name = "None" end) - - let of_elt = function - | `Lifted x -> Some (S.P.of_elt x) - | _ -> None - end - - let name () = S.name ()^" lifted" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - - let startcontext () = S.startcontext () - let startstate v = `Lifted (S.startstate v) - let exitstate v = `Lifted (S.exitstate v) - let morphstate v d = try `Lifted (S.morphstate v (D.unlift d)) with Deadcode -> d - - - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) - } - - let context ctx fd = S.context (conv ctx) fd % D.unlift - - let lift_fun ctx f g h b = - try f @@ h (g (conv ctx)) - with Deadcode -> b - - let sync ctx reason = lift_fun ctx D.lift S.sync ((|>) reason) `Bot - - let enter ctx r f args = - let liftmap = List.map (fun (x,y) -> D.lift x, D.lift y) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) [] - - let paths_as_set ctx = - let liftmap = List.map (fun x -> D.lift x) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) [D.bot ()] (* One dead path instead of none, such that combine_env gets called for functions with dead normal return (and thus longjmpy returns can be correctly handled by lifter). *) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun (x) -> x q) (Queries.Result.bot q) - let assign ctx lv e = lift_fun ctx D.lift S.assign ((|>) e % (|>) lv) `Bot - let vdecl ctx v = lift_fun ctx D.lift S.vdecl ((|>) v) `Bot - let branch ctx e tv = lift_fun ctx D.lift S.branch ((|>) tv % (|>) e) `Bot - let body ctx f = lift_fun ctx D.lift S.body ((|>) f) `Bot - let return ctx r f = lift_fun ctx D.lift S.return ((|>) f % (|>) r) `Bot - let asm ctx = lift_fun ctx D.lift S.asm identity `Bot - let skip ctx = lift_fun ctx D.lift S.skip identity `Bot - let special ctx r f args = lift_fun ctx D.lift S.special ((|>) args % (|>) f % (|>) r) `Bot - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx D.lift (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot - - let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot -end - - -(** Add path sensitivity to a analysis *) -module PathSensitive2 (Spec:Spec) - : Spec - with module G = Spec.G - and module C = Spec.C - and module V = Spec.V -= -struct - module D = - struct - (* TODO is it really worth it to check every time instead of just using sets and joining later? *) - module R = - struct - include Spec.P - type elt = Spec.D.t - end - module J = SetDomain.Joined (Spec.D) - include DisjointDomain.ProjectiveSet (Spec.D) (J) (R) - let name () = "PathSensitive (" ^ name () ^ ")" - - let printXml f x = - let print_one x = - BatPrintf.fprintf f "\n%a" Spec.D.printXml x - in - iter print_one x - end - - module G = Spec.G - module C = Spec.C - module V = Spec.V - module P = UnitP - - let name () = "PathSensitive2("^Spec.name ()^")" - - type marshal = Spec.marshal - let init = Spec.init - let finalize = Spec.finalize - - let startcontext () = Spec.startcontext () - let exitstate v = D.singleton (Spec.exitstate v) - let startstate v = D.singleton (Spec.startstate v) - let morphstate v d = D.map (Spec.morphstate v) d - - let conv ctx x = - let rec ctx' = { ctx with ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx' q) - ; local = x - ; split = (ctx.split % D.singleton) } - in - ctx' - - let context ctx fd l = - if D.cardinal l <> 1 then - failwith "PathSensitive2.context must be called with a singleton set." - else - let x = D.choose l in - Spec.context (conv ctx x) fd x - - - let map ctx f g = - let h x xs = - try D.add (g (f (conv ctx x))) xs - with Deadcode -> xs - in - let d = D.fold h ctx.local (D.empty ()) in - if D.is_bot d then raise Deadcode else d - - let fold' ctx f g h a = - let k x a = - try h a @@ g @@ f @@ conv ctx x - with Deadcode -> a - in - D.fold k ctx.local a - - let assign ctx l e = map ctx Spec.assign (fun h -> h l e ) - let vdecl ctx v = map ctx Spec.vdecl (fun h -> h v) - let body ctx f = map ctx Spec.body (fun h -> h f ) - let return ctx e f = map ctx Spec.return (fun h -> h e f ) - let branch ctx e tv = map ctx Spec.branch (fun h -> h e tv) - let asm ctx = map ctx Spec.asm identity - let skip ctx = map ctx Spec.skip identity - let special ctx l f a = map ctx Spec.special (fun h -> h l f a) - - let event ctx e octx = - let fd1 = D.choose octx.local in - map ctx Spec.event (fun h -> h e (conv octx fd1)) - - let threadenter ctx ~multiple lval f args = - let g xs ys = (List.map (fun y -> D.singleton y) ys) @ xs in - fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] - - let threadspawn ctx ~multiple lval f args fctx = - let fd1 = D.choose fctx.local in - map ctx (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fctx fd1)) - - let sync ctx reason = map ctx Spec.sync (fun h -> h reason) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - (* TODO: handle Invariant path like PathSensitive3? *) - (* join results so that they are sound for all paths *) - let module Result = (val Queries.Result.lattice q) in - fold' ctx Spec.query identity (fun x f -> Result.join x (f q)) (Result.bot ()) - - let enter ctx l f a = - let g xs ys = (List.map (fun (x,y) -> D.singleton x, D.singleton y) ys) @ xs in - fold' ctx Spec.enter (fun h -> h l f a) g [] - - let paths_as_set ctx = - (* Path-sensitivity is only here, not below! *) - let elems = D.elements ctx.local in - List.map (D.singleton) elems - - let combine_env ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in - let k x y = - if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; - try - let r = Spec.combine_env (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; - D.add r y - with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead"; - y - in - let d = D.fold k d (D.bot ()) in - if D.is_bot d then raise Deadcode else d - - let combine_assign ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in - let k x y = - if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; - try - let r = Spec.combine_assign (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; - D.add r y - with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead"; - y - in - let d = D.fold k d (D.bot ()) in - if D.is_bot d then raise Deadcode else d -end - -module DeadBranchLifter (S: Spec): Spec = -struct - include S - - let name () = "DeadBranch (" ^ S.name () ^ ")" - - (* Two global invariants: - 1. S.V -> S.G -- used for S - 2. node -> (exp -> flat bool) -- used for warnings *) - - module V = - struct - include Printable.EitherConf (struct let expand1 = false let expand2 = true end) (S.V) (Node) - let name () = "DeadBranch" - let s x = `Left x - let node x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | `Right _ -> true - end - - module EM = - struct - include MapDomain.MapBot (Basetype.CilExp) (BoolDomain.FlatBool) - let name () = "branches" - end - - module G = - struct - include Lattice.Lift2 (S.G) (EM) - let name () = "deadbranch" - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "DeadBranchLifter.s" - let node = function - | `Bot -> EM.bot () - | `Lifted2 x -> x - | _ -> failwith "DeadBranchLifter.node" - let create_s s = `Lifted1 s - let create_node node = `Lifted2 node - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> - let em = G.node (ctx.global (V.node g)) in - EM.iter (fun exp tv -> - match tv with - | `Lifted tv -> - let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) - let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in - M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv - | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) - M.msg_final Error ~category:Analyzer ~tags:[Category Unsound] "Both branches dead"; - M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp - | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) - | `Top -> (* may be both true and false *) - () - ) em; - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - - (* node vars for dead branches *) - begin match vq with - | Node {node; _} -> - vf (Obj.repr (V.node node)) - | _ -> - () - end - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - let context ctx = S.context (conv ctx) - - let branch ctx exp tv = - if !AnalysisState.postsolving then ( - try - let r = branch ctx exp tv in - (* branch is live *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) - r - with Deadcode -> - (* branch is dead *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) - raise Deadcode - ) - else ( - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) - branch ctx exp tv - ) - - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - module LongjmpLifter (S: Spec): Spec = struct include S From 7f4f5aa7081e9d00f685a813e391ee980b1ed271 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:27:25 +0300 Subject: [PATCH 384/566] Remove LongjmpLifter from SpecLifters --- src/lifters/specLifters.ml | 243 ------------------------------------- 1 file changed, 243 deletions(-) diff --git a/src/lifters/specLifters.ml b/src/lifters/specLifters.ml index 1be137cf8c..3286721bf0 100644 --- a/src/lifters/specLifters.ml +++ b/src/lifters/specLifters.ml @@ -782,246 +782,3 @@ struct let asm ctx = S.asm (conv ctx) let event ctx e octx = S.event (conv ctx) e (conv octx) end - -module LongjmpLifter (S: Spec): Spec = -struct - include S - - let name () = "Longjmp (" ^ S.name () ^ ")" - - module V = - struct - include Printable.Either3Conf (struct let expand1 = false let expand2 = true let expand3 = true end) (S.V) (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C)) - let name () = "longjmp" - let s x = `Left x - let longjmpto x = `Middle x - let longjmpret x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | _ -> false - end - - module G = - struct - include Lattice.Lift2 (S.G) (S.D) - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "LongjmpLifter.s" - let local = function - | `Bot -> S.D.bot () - | `Lifted2 x -> x - | _ -> failwith "LongjmpLifter.local" - let create_s s = `Lifted1 s - let create_local local = `Lifted2 local - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" S.D.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | _ -> - Queries.Result.top q - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | _ -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - (* TODO: vars? *) - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let context ctx = S.context (conv ctx) - - let combine_env ctx lv e f args fc fd f_ask = - let conv_ctx = conv ctx in - let current_fundec = Node.find_fundec ctx.node in - let handle_longjmp (cd, fc, longfd) = - (* This is called per-path. *) - let rec cd_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); - local = cd; - } - in - let longfd_ctx = - (* Inner scope to prevent unsynced longfd_ctx from being used. *) - (* Extra sync like with normal combine. *) - let rec sync_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query sync_ctx q); - local = longfd; - prev_node = Function f; - } - in - let synced = S.sync sync_ctx `Join in - let rec longfd_ctx = - { sync_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query longfd_ctx q); - local = synced; - } - in - longfd_ctx - in - let combined = lazy ( (* does not depend on target, do at most once *) - (* Globals are non-problematic here, as they are always carried around without any issues! *) - (* A combine call is mostly needed to ensure locals have appropriate values. *) - (* Using f from called function on purpose here! Needed? *) - S.combine_env cd_ctx None e f args fc longfd_ctx.local (Analyses.ask_of_ctx longfd_ctx) (* no lval because longjmp return skips return value assignment *) - ) - in - let returned = lazy ( (* does not depend on target, do at most once *) - let rec combined_ctx = - { cd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query combined_ctx q); - local = Lazy.force combined; - } - in - S.return combined_ctx None current_fundec - ) - in - let (active_targets, _) = longfd_ctx.ask ActiveJumpBuf in - let valid_targets = cd_ctx.ask ValidLongJmp in - let handle_target target = match target with - | JmpBufDomain.BufferEntryOrTop.AllTargets -> () (* The warning is already emitted at the point where the longjmp happens *) - | Target (target_node, target_context) -> - let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Fun: Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force combined)) - (* No need to propagate this outwards here, the set of valid longjumps is part of the context, we can never have the same context setting the longjmp multiple times *) - ) - (* Appropriate setjmp is not in current function & current context *) - else if JmpBufDomain.JmpBufSet.mem target valid_targets then - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) - else - (* It actually is not handled here but was propagated here spuriously, we already warned at the location where this issue is caused *) - (* As the validlongjumps inside the callee is a a superset of the ones inside the caller *) - () - in - JmpBufDomain.JmpBufSet.iter handle_target active_targets - in - if M.tracing then M.tracel "longjmp" "longfd getg %a" CilType.Fundec.pretty f; - let longfd = G.local (ctx.global (V.longjmpret (f, Option.get fc))) in - if M.tracing then M.tracel "longjmp" "longfd %a" D.pretty longfd; - if not (D.is_bot longfd) then - handle_longjmp (ctx.local, fc, longfd); - S.combine_env (conv_ctx) lv e f args fc fd f_ask - - let combine_assign ctx lv e f args fc fd f_ask = - S.combine_assign (conv ctx) lv e f args fc fd f_ask - - let special ctx lv f args = - let conv_ctx = conv ctx in - match (LibraryFunctions.find f).special args with - | Setjmp {env} -> - (* Handling of returning for the first time *) - let normal_return = S.special conv_ctx lv f args in - let jmp_return = G.local (ctx.global (V.longjmpto (ctx.prev_node, ctx.context ()))) in - if S.D.is_bot jmp_return then - normal_return - else ( - let rec jmp_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query jmp_ctx q); - local = jmp_return; - } - in - let longjmped = S.event jmp_ctx (Events.Longjmped {lval=lv}) jmp_ctx in - S.D.join normal_return longjmped - ) - | Longjmp {env; value} -> - let current_fundec = Node.find_fundec ctx.node in - let handle_path path = ( - let rec path_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query path_ctx q); - local = path; - } - in - let specialed = lazy ( (* does not depend on target, do at most once *) - S.special path_ctx lv f args - ) - in - let returned = lazy ( (* does not depend on target, do at most once *) - let rec specialed_ctx = - { path_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query specialed_ctx q); - local = Lazy.force specialed; - } - in - S.return specialed_ctx None current_fundec - ) - in - (* Eval `env` again to avoid having to construct bespoke ctx to ask *) - let targets = path_ctx.ask (EvalJumpBuf env) in - let valid_targets = path_ctx.ask ValidLongJmp in - if M.tracing then Messages.tracel "longjmp" "Jumping to %a" JmpBufDomain.JmpBufSet.pretty targets; - let handle_target target = match target with - | JmpBufDomain.BufferEntryOrTop.AllTargets -> - M.warn ~category:Imprecise "Longjmp to potentially invalid target, as contents of buffer %a may be unknown! (imprecision due to heap?)" d_exp env; - M.msg_final Error ~category:Unsound ~tags:[Category Imprecise; Category Call] "Longjmp to unknown target ignored" - | Target (target_node, target_context) -> - let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force specialed)) - ) - else if JmpBufDomain.JmpBufSet.mem target valid_targets then ( - if M.tracing then Messages.tracel "longjmp" "Longjmp to somewhere else, side-effect to %i" (S.C.hash (ctx.context ())); - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) - ) - else - M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target! (Target %a in Function %a which may have already returned or is in a different thread)" Node.pretty target_node CilType.Fundec.pretty target_fundec - in - if JmpBufDomain.JmpBufSet.is_empty targets then - M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target (%a is bot?!)" d_exp env - else - JmpBufDomain.JmpBufSet.iter handle_target targets - ) - in - List.iter handle_path (S.paths_as_set conv_ctx); - if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( - AnalysisState.svcomp_may_not_terminate := true; - M.warn ~category:Termination "The program might not terminate! (Longjmp)" - ); - S.D.bot () - | _ -> S.special conv_ctx lv f args - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end From 3b285e3d7f6cee1ff1159cc288ab7f39f3443411 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 15:40:47 +0300 Subject: [PATCH 385/566] Document Spec lifter modules --- src/framework/control.ml | 4 ++-- src/lifters/longjmpLifter.ml | 2 +- src/lifters/longjmpLifter.mli | 3 +++ src/lifters/recursionTermLifter.ml | 3 +-- src/lifters/recursionTermLifter.mli | 3 +++ src/lifters/specLifters.ml | 2 ++ 6 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 src/lifters/longjmpLifter.mli create mode 100644 src/lifters/recursionTermLifter.mli diff --git a/src/framework/control.ml b/src/framework/control.ml index a2fb7c01e9..1d0ebb869b 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -40,8 +40,8 @@ let spec_module: (module Spec) Lazy.t = lazy ( (* 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 (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) - |> lift true (module LongjmpLifter.LongjmpLifter) - |> lift termination_enabled (module RecursionTermLifter.RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) + |> lift true (module LongjmpLifter.Lifter) + |> lift termination_enabled (module RecursionTermLifter.Lifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) ) in GobConfig.building_spec := false; diff --git a/src/lifters/longjmpLifter.ml b/src/lifters/longjmpLifter.ml index 1150bc1bca..1a28085abe 100644 --- a/src/lifters/longjmpLifter.ml +++ b/src/lifters/longjmpLifter.ml @@ -3,7 +3,7 @@ open Analyses open GobConfig -module LongjmpLifter (S: Spec): Spec = +module Lifter (S: Spec): Spec = struct include S diff --git a/src/lifters/longjmpLifter.mli b/src/lifters/longjmpLifter.mli new file mode 100644 index 0000000000..5fd74907bc --- /dev/null +++ b/src/lifters/longjmpLifter.mli @@ -0,0 +1,3 @@ +(** Analysis lifter for [longjmp] and [setjmp] support. *) + +module Lifter: Analyses.Spec2Spec diff --git a/src/lifters/recursionTermLifter.ml b/src/lifters/recursionTermLifter.ml index 3de3810569..2d9f45de60 100644 --- a/src/lifters/recursionTermLifter.ml +++ b/src/lifters/recursionTermLifter.ml @@ -2,8 +2,7 @@ open GoblintCil open Analyses -(** Add cycle detection in the context-sensitive dynamic function call graph to an analysis *) -module RecursionTermLifter (S: Spec) +module Lifter (S: Spec) : Spec with module D = S.D and module C = S.C = diff --git a/src/lifters/recursionTermLifter.mli b/src/lifters/recursionTermLifter.mli new file mode 100644 index 0000000000..059cea658f --- /dev/null +++ b/src/lifters/recursionTermLifter.mli @@ -0,0 +1,3 @@ +(** Cycle detection in the context-sensitive dynamic function call graph of an analysis. *) + +module Lifter: Analyses.Spec2Spec diff --git a/src/lifters/specLifters.ml b/src/lifters/specLifters.ml index 3286721bf0..4ce158b8ac 100644 --- a/src/lifters/specLifters.ml +++ b/src/lifters/specLifters.ml @@ -1,3 +1,5 @@ +(** Various simple and old analysis lifters. *) + open Batteries open GoblintCil open Analyses From bcb15a1fa360ff8e8fca50deda55ae628fb3e51a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 25 Sep 2024 15:35:32 +0300 Subject: [PATCH 386/566] Bump CIL with potential opam install fix --- goblint.opam | 2 +- goblint.opam.locked | 2 +- goblint.opam.template | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/goblint.opam b/goblint.opam index 0346e78252..b1f1ee97d0 100644 --- a/goblint.opam +++ b/goblint.opam @@ -97,7 +97,7 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#73d02511a0366816d428853634fb939bd2f0a1b7" ] + [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.locked b/goblint.opam.locked index dcf81b7a53..97a8385312 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -140,7 +140,7 @@ post-messages: [ pin-depends: [ [ "goblint-cil.2.0.4" - "git+https://github.com/goblint/cil.git#73d02511a0366816d428853634fb939bd2f0a1b7" + "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" ] ] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.template b/goblint.opam.template index 2a5d5690fc..a8a46aa108 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -2,7 +2,7 @@ # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#73d02511a0366816d428853634fb939bd2f0a1b7" ] + [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} From f7e4462dda207367138b259fe0751ee061e86032 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 25 Sep 2024 15:48:25 +0300 Subject: [PATCH 387/566] Add cram test for SV-COMP architecture --- tests/regression/29-svcomp/36-svcomp-arch.c | 8 ++++++++ tests/regression/29-svcomp/36-svcomp-arch.t | 22 +++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/regression/29-svcomp/36-svcomp-arch.c create mode 100644 tests/regression/29-svcomp/36-svcomp-arch.t diff --git a/tests/regression/29-svcomp/36-svcomp-arch.c b/tests/regression/29-svcomp/36-svcomp-arch.c new file mode 100644 index 0000000000..ea68ba187c --- /dev/null +++ b/tests/regression/29-svcomp/36-svcomp-arch.c @@ -0,0 +1,8 @@ +// CRAM +#include + +int main() { + long k = INT_MAX; + long n = k * k; + return 0; +} diff --git a/tests/regression/29-svcomp/36-svcomp-arch.t b/tests/regression/29-svcomp/36-svcomp-arch.t new file mode 100644 index 0000000000..a0715e3872 --- /dev/null +++ b/tests/regression/29-svcomp/36-svcomp-arch.t @@ -0,0 +1,22 @@ +There should be overflow on ILP32: + + $ goblint --enable ana.sv-comp.enabled --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" --set exp.architecture 32bit 36-svcomp-arch.c + [Info] Setting "ana.int.interval" to true + [Info] SV-COMP specification: CHECK( init(main()), LTL(G ! overflow) ) + [Warning][Integer > Overflow][CWE-190] Signed integer overflow (36-svcomp-arch.c:6:8-6:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 4 + dead: 0 + total lines: 4 + SV-COMP result: unknown + +There shouldn't be an overflow on LP64: + + $ goblint --enable ana.sv-comp.enabled --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" --set exp.architecture 64bit 36-svcomp-arch.c + [Info] Setting "ana.int.interval" to true + [Info] SV-COMP specification: CHECK( init(main()), LTL(G ! overflow) ) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 4 + dead: 0 + total lines: 4 + SV-COMP result: true From 701f1aedfba5ba58be8d00cc245a76977a7cba70 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 26 Sep 2024 12:24:12 +0300 Subject: [PATCH 388/566] Document scope of regression test meta annotations --- docs/developer-guide/testing.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/developer-guide/testing.md b/docs/developer-guide/testing.md index a336228fde..9444a96007 100644 --- a/docs/developer-guide/testing.md +++ b/docs/developer-guide/testing.md @@ -79,6 +79,9 @@ Comments at the end of lines can also indicate metaproperties: | `NOTIMEOUT` | Analyer terminates | | `CRAM` | Automatic checks are only in corresponding Cram test | +These comments only document the intention of the test (if there are no other checks in the test). +Analyzer crash, fixpoint error and non-termination are checked even when there are other checks. + ## Cram Tests [Cram-style tests](https://dune.readthedocs.io/en/stable/tests.html#cram-tests) are also used to verify that existing functionality hasn't been broken. They check the complete standard output of running the Goblint binary with specified command-line arguments. From 8cce1c945213f10d2be0809dc4411ca644d4acb5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 26 Sep 2024 12:26:03 +0300 Subject: [PATCH 389/566] Comment on negative line numbers in update_suite --- scripts/update_suite.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index e760cdd721..b21fb4b532 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -326,6 +326,7 @@ def parse_tests (lines) if obj =~ /#line ([0-9]+).*$/ then i = $1.to_i - 1 end + # test annotations are stored by line, use impossible line -42 for these metaproperties if obj =~ /NOCRASH/ then tests[-42] = "nocrash" elsif obj =~ /FIXPOINT/ then @@ -369,6 +370,7 @@ def parse_tests (lines) end end case lines[0] + # test annotations are stored by line, use impossible line -1 for these whole-program properties when /NONTERM/ tests[-1] = "nonterm" when /TERM/ From 54eb6f6bf318230182c12a56e5740ce654dd980f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 26 Sep 2024 12:27:15 +0300 Subject: [PATCH 390/566] Add CRAM annotation to 00-sanity/33-hoare-over-paths Although there are asserts, so update_suite doesn't complain about no checks. --- tests/regression/00-sanity/33-hoare-over-paths.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/00-sanity/33-hoare-over-paths.c b/tests/regression/00-sanity/33-hoare-over-paths.c index 4a809685ed..e3af40cbb3 100644 --- a/tests/regression/00-sanity/33-hoare-over-paths.c +++ b/tests/regression/00-sanity/33-hoare-over-paths.c @@ -1,7 +1,7 @@ // PARAM: --set ana.path_sens[+] mutex #include #include - +// CRAM pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int main() { From f1b6157a3b0846d48a69529577a82045acd1d5cc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 27 Sep 2024 10:59:54 +0300 Subject: [PATCH 391/566] Add "ERROR (both branches dead)" verdict for SV-COMP (closes #1576) --- src/common/framework/analysisState.ml | 5 +++++ src/goblint.ml | 1 + src/lifters/specLifters.ml | 5 +++++ src/util/server.ml | 1 + src/witness/witness.ml | 7 ++++--- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/common/framework/analysisState.ml b/src/common/framework/analysisState.ml index d1764c839f..96816b8529 100644 --- a/src/common/framework/analysisState.ml +++ b/src/common/framework/analysisState.ml @@ -36,3 +36,8 @@ let postsolving = ref false (* None if verification is disabled, Some true if verification succeeded, Some false if verification failed *) let verified : bool option ref = ref None + +let unsound_both_branches_dead: bool option ref = ref None +(** [Some true] if unsound both branches dead occurs in analysis results. + [Some false] if it doesn't occur. + [None] if [ana.dead-code.branches] option is disabled and this isn't checked. *) \ No newline at end of file diff --git a/src/goblint.ml b/src/goblint.ml index 52b9bbdfc0..bdfcadd3d2 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -65,6 +65,7 @@ let main () = do_gobview file; do_stats (); Goblint_timing.teardown_tef (); + (* TODO: generalize exit codes for AnalysisState.unsound_both_branches_dead? *) if !AnalysisState.verified = Some false then exit 3 (* verifier failed! *) ) with diff --git a/src/lifters/specLifters.ml b/src/lifters/specLifters.ml index 4ce158b8ac..e8292bedc0 100644 --- a/src/lifters/specLifters.ml +++ b/src/lifters/specLifters.ml @@ -695,6 +695,10 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end + let init marshal = + init marshal; + AnalysisState.unsound_both_branches_dead := Some false + let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = { ctx with global = (fun v -> G.s (ctx.global (V.s v))); @@ -717,6 +721,7 @@ struct let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) + AnalysisState.unsound_both_branches_dead := Some true; M.msg_final Error ~category:Analyzer ~tags:[Category Unsound] "Both branches dead"; M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) diff --git a/src/util/server.ml b/src/util/server.ml index 7b603e7c6e..1bdc3e1ea9 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -320,6 +320,7 @@ let () = let process { reset } serve = try analyze serve ~reset; + (* TODO: generalize VerifyError for AnalysisState.unsound_both_branches_dead *) {status = if !AnalysisState.verified = Some false then VerifyError else Success} with | Sys.Break -> diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 7b0213b601..5da46a1011 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -691,9 +691,10 @@ struct ) let write yaml_validate_result entrystates = - match !AnalysisState.verified with - | Some false -> print_svcomp_result "ERROR (verify)" - | _ -> + match !AnalysisState.verified, !AnalysisState.unsound_both_branches_dead with + | _, Some true -> print_svcomp_result "ERROR (both branches dead)" + | Some false, _ -> print_svcomp_result "ERROR (verify)" + | _, _ -> match yaml_validate_result with | Some (Stdlib.Error msg) -> print_svcomp_result ("ERROR (" ^ msg ^ ")") From ea2f61645301663b54bbce800a258d9dc7a4cfc8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 27 Sep 2024 12:16:06 +0300 Subject: [PATCH 392/566] Use ptrdiff_ikind instead of ILong for ana.arrayoob checks --- src/cdomain/value/cdomains/arrayDomain.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cdomain/value/cdomains/arrayDomain.ml b/src/cdomain/value/cdomains/arrayDomain.ml index e1cfb96425..4192489c3a 100644 --- a/src/cdomain/value/cdomains/arrayDomain.ml +++ b/src/cdomain/value/cdomains/arrayDomain.ml @@ -834,7 +834,7 @@ end let array_oob_check ( type a ) (module Idx: IntDomain.Z with type t = a) (x, l) (e, v) = if GobConfig.get_bool "ana.arrayoob" then (* The purpose of the following 2 lines is to give the user extra info about the array oob *) let idx_before_end = Idx.to_bool (Idx.lt v l) (* check whether index is before the end of the array *) - and idx_after_start = Idx.to_bool (Idx.ge v (Idx.of_int Cil.ILong Z.zero)) in (* check whether the index is non-negative *) + and idx_after_start = Idx.to_bool (Idx.ge v (Idx.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero)) in (* check whether the index is non-negative *) (* For an explanation of the warning types check the Pull Request #255 *) match(idx_after_start, idx_before_end) with | Some true, Some true -> (* Certainly in bounds on both sides.*) From e35cc3cdc2d04ec230c4b7325cd17cfeaccd0c09 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 18 Jun 2024 18:04:08 +0300 Subject: [PATCH 393/566] Log instead of printing to stderr --- src/util/loopUnrolling.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 07c20cb574..ad964974d0 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -274,8 +274,7 @@ let fixedLoopSize loopStatement func = else constBefore var loopStatement func >>= fun start -> assignmentDifference loopStatement var >>= fun diff -> - Logs.debug "comparison: "; - Pretty.fprint stderr (dn_exp () comparison) ~width:max_int; + Logs.debug "comparison: %a" CilType.Exp.pretty comparison; Logs.debug ""; Logs.debug "variable: "; Logs.debug "%s" var.vname; From 96b5d2024c6859371bb38e451a9111beb2189f8b Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 18 Jun 2024 18:05:45 +0300 Subject: [PATCH 394/566] Move debug messages to one line instead of printing 2 separate msgs --- src/util/loopUnrolling.ml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index ad964974d0..7c59cc7473 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -275,21 +275,16 @@ let fixedLoopSize loopStatement func = constBefore var loopStatement func >>= fun start -> assignmentDifference loopStatement var >>= fun diff -> Logs.debug "comparison: %a" CilType.Exp.pretty comparison; - Logs.debug ""; - Logs.debug "variable: "; - Logs.debug "%s" var.vname; - Logs.debug "start:"; - Logs.debug "%s" @@ Z.to_string start; - Logs.debug "diff:"; - Logs.debug "%s" @@ Z.to_string diff; + Logs.debug "variable: %s" var.vname; + Logs.debug "start: %s" @@ Z.to_string start; + Logs.debug "diff: %s" @@ Z.to_string diff; let iterations = loopIterations start diff comparison in match iterations with | None -> Logs.debug "iterations failed"; None | Some s -> try let s' = Z.to_int s in - Logs.debug "iterations:"; - Logs.debug "%d" s'; + Logs.debug "iterations: %d" s'; Some s' with Z.Overflow -> Logs.debug "iterations too big for integer"; None From 8af3f8c92cd75f6f37b9481ecce8f6ea4863b0b6 Mon Sep 17 00:00:00 2001 From: Karoliine Holter <44437975+karoliineh@users.noreply.github.com> Date: Thu, 20 Jun 2024 12:59:00 +0300 Subject: [PATCH 395/566] Use GobZ.pretty instead Z.to_string Co-authored-by: Simmo Saan --- src/util/loopUnrolling.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 7c59cc7473..80dd110e5f 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -276,8 +276,8 @@ let fixedLoopSize loopStatement func = assignmentDifference loopStatement var >>= fun diff -> Logs.debug "comparison: %a" CilType.Exp.pretty comparison; Logs.debug "variable: %s" var.vname; - Logs.debug "start: %s" @@ Z.to_string start; - Logs.debug "diff: %s" @@ Z.to_string diff; + Logs.debug "start: %a" GobZ.pretty start; + Logs.debug "diff: %a" GobZ.pretty diff; let iterations = loopIterations start diff comparison in match iterations with | None -> Logs.debug "iterations failed"; None From b3eaeda2e531d9960c069d3c4731c18b71e7f6b5 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Sep 2024 20:41:32 +0300 Subject: [PATCH 396/566] Make getLoopVar return loop bound goal in addition to the varinfo --- src/util/loopUnrolling.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 80dd110e5f..1a6f5d3344 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -250,9 +250,9 @@ let fixedLoopSize loopStatement func = !compOption with | WrongOrMultiple -> None in let getLoopVar = function - | BinOp (op, (Const (CInt _ )), Lval ((Var info), NoOffset), (TInt _)) - | BinOp (op, Lval ((Var info), NoOffset), (Const (CInt _ )), (TInt _)) when isCompare op && not info.vglob-> - Some info + | BinOp (op, (Const (CInt (goal, _, _) )), Lval ((Var info), NoOffset), (TInt _)) + | BinOp (op, Lval ((Var info), NoOffset), (Const (CInt (goal, _, _) )), (TInt _)) when isCompare op && not info.vglob-> + Some (info, goal) | _ -> None in let getsPointedAt var = try let visitor = new isPointedAtVisitor(var) in @@ -268,7 +268,7 @@ let fixedLoopSize loopStatement func = in findBreakComparison >>= fun comparison -> - getLoopVar comparison >>= fun var -> + getLoopVar comparison >>= fun (var, goal) -> if getsPointedAt var then None else From 6ccd3c10be5ec2fa890ec3799b0b536dc5121b5b Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Sep 2024 21:48:51 +0300 Subject: [PATCH 397/566] Make sure goal is found from the exact same comparison exp where the var was found --- src/util/loopUnrolling.ml | 64 ++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 1a6f5d3344..92d5dab50f 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -210,35 +210,27 @@ let constBefore var loop f = in fst @@ lastAssignmentToVarBeforeLoop (Some Z.zero) f.sbody.bstmts (*the top level call should never return false*) -let rec loopIterations start diff comp = - let flip = function - | Lt -> Gt - | Gt -> Lt - | Ge -> Le - | Le -> Ge - | s -> s - in let loopIterations' goal shouldBeExact = - let range = Z.sub goal start in - if Z.equal diff Z.zero || Z.equal range Z.zero || (Z.gt diff Z.zero && Z.lt range Z.zero) || (Z.lt diff Z.zero && Z.gt range Z.zero) then - None (*unfitting parameters*) - else ( - let roundedDown = Z.div range diff in - let isExact = Z.equal (Z.mul roundedDown diff) range in - if isExact then - Some roundedDown - else if shouldBeExact then - None - else - Some (Z.succ roundedDown) - ) - in - match comp with - | BinOp (op, (Const _ as c), var, t) -> loopIterations start diff (BinOp (flip op, var, c, t)) - | BinOp (Lt, _, (Const (CInt (cint,_,_) )), _) -> if Z.lt diff Z.zero then None else loopIterations' cint false - | BinOp (Gt, _, (Const (CInt (cint,_,_) )), _) -> if Z.gt diff Z.zero then None else loopIterations' cint false - | BinOp (Le, _, (Const (CInt (cint,_,_) )), _) -> if Z.lt diff Z.zero then None else loopIterations' (Z.succ cint) false - | BinOp (Ge, _, (Const (CInt (cint,_,_) )), _) -> if Z.gt diff Z.zero then None else loopIterations' (Z.pred cint) false - | BinOp (Ne, _, (Const (CInt (cint,_,_) )), _) -> loopIterations' cint true +let loopIterations start diff goal shouldBeExact = + let range = Z.sub goal start in + if Z.equal diff Z.zero || Z.equal range Z.zero || (Z.gt diff Z.zero && Z.lt range Z.zero) || (Z.lt diff Z.zero && Z.gt range Z.zero) then + None (*unfitting parameters*) + else ( + let roundedDown = Z.div range diff in + let isExact = Z.equal (Z.mul roundedDown diff) range in + if isExact then + Some roundedDown + else if shouldBeExact then + None + else + Some (Z.succ roundedDown) + ) +let adjustGoal diff goal op = + match op with + | Lt -> if Z.lt diff Z.zero then None else Some goal + | Gt -> if Z.gt diff Z.zero then None else Some goal + | Le -> if Z.lt diff Z.zero then None else Some (Z.succ goal) + | Ge -> if Z.gt diff Z.zero then None else Some (Z.pred goal) + | Ne -> Some goal | _ -> failwith "unexpected comparison in loopIterations" let ( >>= ) = Option.bind @@ -250,9 +242,12 @@ let fixedLoopSize loopStatement func = !compOption with | WrongOrMultiple -> None in let getLoopVar = function - | BinOp (op, (Const (CInt (goal, _, _) )), Lval ((Var info), NoOffset), (TInt _)) - | BinOp (op, Lval ((Var info), NoOffset), (Const (CInt (goal, _, _) )), (TInt _)) when isCompare op && not info.vglob-> - Some (info, goal) + | BinOp (op, (Const (CInt (goal, _, _) )), Lval ((Var varinfo), NoOffset), (TInt _)) when isCompare op && not varinfo.vglob -> + (* TODO: define isCompare and flip in cilfacade and refactor to use instead of the many separately defined similar functions *) + let flip = function | Lt -> Gt | Gt -> Lt | Ge -> Le | Le -> Ge | s -> s in + Some (flip op, varinfo, goal) + | BinOp (op, Lval ((Var varinfo), NoOffset), (Const (CInt (goal, _, _) )), (TInt _)) when isCompare op && not varinfo.vglob -> + Some (op, varinfo, goal) | _ -> None in let getsPointedAt var = try let visitor = new isPointedAtVisitor(var) in @@ -268,7 +263,7 @@ let fixedLoopSize loopStatement func = in findBreakComparison >>= fun comparison -> - getLoopVar comparison >>= fun (var, goal) -> + getLoopVar comparison >>= fun (op, var, goal) -> if getsPointedAt var then None else @@ -278,7 +273,8 @@ let fixedLoopSize loopStatement func = Logs.debug "variable: %s" var.vname; Logs.debug "start: %a" GobZ.pretty start; Logs.debug "diff: %a" GobZ.pretty diff; - let iterations = loopIterations start diff comparison in + adjustGoal diff goal op >>= fun goal -> + let iterations = loopIterations start diff goal (op=Ne) in match iterations with | None -> Logs.debug "iterations failed"; None | Some s -> From cbf74aea86bbc760bf6efdb9bd80412ad65fd3a7 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Sep 2024 21:52:11 +0300 Subject: [PATCH 398/566] Add arguments to functions --- src/util/loopUnrolling.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 92d5dab50f..c480f26127 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -235,7 +235,7 @@ let adjustGoal diff goal op = let ( >>= ) = Option.bind let fixedLoopSize loopStatement func = - let findBreakComparison = try (*find a single break in the else branch of a toplevel if*) + let findBreakComparison loopStatement = try (*find a single break in the else branch of a toplevel if*) let compOption = ref None in let visitor = new findBreakVisitor(compOption) in ignore @@ visitCilBlock visitor (loopBody loopStatement); @@ -249,7 +249,7 @@ let fixedLoopSize loopStatement func = | BinOp (op, Lval ((Var varinfo), NoOffset), (Const (CInt (goal, _, _) )), (TInt _)) when isCompare op && not varinfo.vglob -> Some (op, varinfo, goal) | _ -> None - in let getsPointedAt var = try + in let getsPointedAt var func = try let visitor = new isPointedAtVisitor(var) in ignore @@ visitCilFunction visitor func; false @@ -262,9 +262,9 @@ let fixedLoopSize loopStatement func = with | WrongOrMultiple -> None in - findBreakComparison >>= fun comparison -> + findBreakComparison loopStatement >>= fun comparison -> getLoopVar comparison >>= fun (op, var, goal) -> - if getsPointedAt var then + if getsPointedAt var func then None else constBefore var loopStatement func >>= fun start -> From 1eeae94041379b6ecc4d335663b4b6555a8f3f8b Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Sep 2024 22:03:13 +0300 Subject: [PATCH 399/566] Move functions defined in fixedLoopSize to top level --- src/util/loopUnrolling.ml | 84 ++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index c480f26127..ffd1c10418 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -210,6 +210,51 @@ let constBefore var loop f = in fst @@ lastAssignmentToVarBeforeLoop (Some Z.zero) f.sbody.bstmts (*the top level call should never return false*) +(*find a single break in the else branch of a toplevel if*) +let findBreakComparison loopStatement = + try + let compOption = ref None in + let visitor = new findBreakVisitor (compOption) in + ignore @@ visitCilBlock visitor (loopBody loopStatement); + !compOption + with WrongOrMultiple -> + None + +let getLoopVar = function + | BinOp (op, (Const (CInt (goal, _, _) )), Lval ((Var varinfo), NoOffset), (TInt _)) when isCompare op && not varinfo.vglob -> + (* TODO: define isCompare and flip in cilfacade and refactor to use instead of the many separately defined similar functions *) + let flip = function | Lt -> Gt | Gt -> Lt | Ge -> Le | Le -> Ge | s -> s in + Some (flip op, varinfo, goal) + | BinOp (op, Lval ((Var varinfo), NoOffset), (Const (CInt (goal, _, _) )), (TInt _)) when isCompare op && not varinfo.vglob -> + Some (op, varinfo, goal) + | _ -> None + +let getsPointedAt var func = + try + let visitor = new isPointedAtVisitor (var) in + ignore @@ visitCilFunction visitor func; + false + with Found -> + true + +let assignmentDifference loop var = + try + let diff = ref None in + let visitor = new findAssignmentConstDiff (diff, var) in + ignore @@ visitCilStmt visitor loop; + !diff + with WrongOrMultiple -> + None + +let adjustGoal diff goal op = + match op with + | Lt -> if Z.lt diff Z.zero then None else Some goal + | Gt -> if Z.gt diff Z.zero then None else Some goal + | Le -> if Z.lt diff Z.zero then None else Some (Z.succ goal) + | Ge -> if Z.gt diff Z.zero then None else Some (Z.pred goal) + | Ne -> Some goal + | _ -> failwith "unexpected comparison in loopIterations" + let loopIterations start diff goal shouldBeExact = let range = Z.sub goal start in if Z.equal diff Z.zero || Z.equal range Z.zero || (Z.gt diff Z.zero && Z.lt range Z.zero) || (Z.lt diff Z.zero && Z.gt range Z.zero) then @@ -224,44 +269,9 @@ let loopIterations start diff goal shouldBeExact = else Some (Z.succ roundedDown) ) -let adjustGoal diff goal op = - match op with - | Lt -> if Z.lt diff Z.zero then None else Some goal - | Gt -> if Z.gt diff Z.zero then None else Some goal - | Le -> if Z.lt diff Z.zero then None else Some (Z.succ goal) - | Ge -> if Z.gt diff Z.zero then None else Some (Z.pred goal) - | Ne -> Some goal - | _ -> failwith "unexpected comparison in loopIterations" let ( >>= ) = Option.bind let fixedLoopSize loopStatement func = - let findBreakComparison loopStatement = try (*find a single break in the else branch of a toplevel if*) - let compOption = ref None in - let visitor = new findBreakVisitor(compOption) in - ignore @@ visitCilBlock visitor (loopBody loopStatement); - !compOption - with | WrongOrMultiple -> None - in let getLoopVar = function - | BinOp (op, (Const (CInt (goal, _, _) )), Lval ((Var varinfo), NoOffset), (TInt _)) when isCompare op && not varinfo.vglob -> - (* TODO: define isCompare and flip in cilfacade and refactor to use instead of the many separately defined similar functions *) - let flip = function | Lt -> Gt | Gt -> Lt | Ge -> Le | Le -> Ge | s -> s in - Some (flip op, varinfo, goal) - | BinOp (op, Lval ((Var varinfo), NoOffset), (Const (CInt (goal, _, _) )), (TInt _)) when isCompare op && not varinfo.vglob -> - Some (op, varinfo, goal) - | _ -> None - in let getsPointedAt var func = try - let visitor = new isPointedAtVisitor(var) in - ignore @@ visitCilFunction visitor func; - false - with | Found -> true - in let assignmentDifference loop var = try - let diff = ref None in - let visitor = new findAssignmentConstDiff(diff, var) in - ignore @@ visitCilStmt visitor loop; - !diff - with | WrongOrMultiple -> None - in - findBreakComparison loopStatement >>= fun comparison -> getLoopVar comparison >>= fun (op, var, goal) -> if getsPointedAt var func then @@ -282,7 +292,9 @@ let fixedLoopSize loopStatement func = let s' = Z.to_int s in Logs.debug "iterations: %d" s'; Some s' - with Z.Overflow -> Logs.debug "iterations too big for integer"; None + with Z.Overflow -> + Logs.debug "iterations too big for integer"; + None class arrayVisitor = object From ee7734dec5bfe37dce5968c4aa0cb5f52443478a Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Sep 2024 22:13:55 +0300 Subject: [PATCH 400/566] Use let* option monad syntax from GobOption.Syntax in LoopUnrolling Co-authored-by: Simmo Saan --- src/util/loopUnrolling.ml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index ffd1c10418..f4e97e98bd 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -270,21 +270,21 @@ let loopIterations start diff goal shouldBeExact = Some (Z.succ roundedDown) ) -let ( >>= ) = Option.bind let fixedLoopSize loopStatement func = - findBreakComparison loopStatement >>= fun comparison -> - getLoopVar comparison >>= fun (op, var, goal) -> + let open GobOption.Syntax in + let* comparison = findBreakComparison loopStatement in + let* op, var, goal = getLoopVar comparison in if getsPointedAt var func then None else - constBefore var loopStatement func >>= fun start -> - assignmentDifference loopStatement var >>= fun diff -> + let* start = constBefore var loopStatement func in + let* diff = assignmentDifference loopStatement var in + let* goal = adjustGoal diff goal op in + let iterations = loopIterations start diff goal (op=Ne) in Logs.debug "comparison: %a" CilType.Exp.pretty comparison; Logs.debug "variable: %s" var.vname; Logs.debug "start: %a" GobZ.pretty start; Logs.debug "diff: %a" GobZ.pretty diff; - adjustGoal diff goal op >>= fun goal -> - let iterations = loopIterations start diff goal (op=Ne) in match iterations with | None -> Logs.debug "iterations failed"; None | Some s -> From da52931aef09c593278481bf6547e35d4cdcf9c5 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 18 Jun 2024 18:03:36 +0300 Subject: [PATCH 401/566] Call findAssignmentConstDiff on loops body instead the loop itself --- src/util/loopUnrolling.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 07c20cb574..ea92b0d1e6 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -262,7 +262,7 @@ let fixedLoopSize loopStatement func = in let assignmentDifference loop var = try let diff = ref None in let visitor = new findAssignmentConstDiff(diff, var) in - ignore @@ visitCilStmt visitor loop; + ignore @@ visitCilBlock visitor loop; !diff with | WrongOrMultiple -> None in @@ -273,7 +273,7 @@ let fixedLoopSize loopStatement func = None else constBefore var loopStatement func >>= fun start -> - assignmentDifference loopStatement var >>= fun diff -> + assignmentDifference (loopBody loopStatement) var >>= fun diff -> Logs.debug "comparison: "; Pretty.fprint stderr (dn_exp () comparison) ~width:max_int; Logs.debug ""; From 31c474cda6ad289e24df95f11865c0ea0dea7b73 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Sep 2024 20:04:50 +0300 Subject: [PATCH 402/566] Do not unroll loops in goblint stub functions --- src/util/loopUnrolling.ml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index ea92b0d1e6..390fb6f9a9 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -468,6 +468,8 @@ class loopUnrollingVisitor(func, totalLoops) = object end let unroll_loops fd totalLoops = - Cil.populateLabelAlphaTable fd; - let thisVisitor = new loopUnrollingVisitor(fd, totalLoops) in - ignore (visitCilFunction thisVisitor fd) + if not (Cil.hasAttribute "goblint_stub" fd.svar.vattr) then ( + Cil.populateLabelAlphaTable fd; + let thisVisitor = new loopUnrollingVisitor(fd, totalLoops) in + ignore (visitCilFunction thisVisitor fd) + ) From 446da4f340a3b456c9cb134380a9c45ca9a370b5 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Sep 2024 15:12:49 +0300 Subject: [PATCH 403/566] Simplify fixed loop size heuristics --- src/util/loopUnrolling.ml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index ea92b0d1e6..f8b53bed1c 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -347,15 +347,11 @@ let loop_unrolling_factor loopStatement func totalLoops = Found -> true in (*unroll up to near an instruction count, higher if the loop uses malloc/lock/threads *) - let targetInstructions = if unrollFunctionCalled then 50 else 25 in let loopStats = AutoTune0.collectFactors visitCilStmt loopStatement in if loopStats.instructions > 0 then - let fixedLoop = fixedLoopSize loopStatement func in - (* Unroll at least 10 times if there are only few (17?) loops *) - let unroll_min = if totalLoops < 17 && AutoTune0.isActivated "forceLoopUnrollForFewLoops" then 10 else 0 in - match fixedLoop with - | Some i -> if i * loopStats.instructions < 100 then (Logs.debug "fixed loop size"; i) else max unroll_min (100 / loopStats.instructions) - | _ -> max unroll_min (targetInstructions / loopStats.instructions) + match fixedLoopSize loopStatement func with + | Some i when i <= 20 -> Logs.debug "fixed loop size %d" i; i + | _ -> 4 else (* Don't unroll empty (= while(1){}) loops*) 0 From e8f51dcaa712c619b4e7b25c26081c6229a55a9f Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Sep 2024 19:53:38 +0300 Subject: [PATCH 404/566] Remove unused loopUnrollingCallVisitor --- src/util/loopUnrolling.ml | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index f8b53bed1c..0872eb9b1c 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -305,48 +305,9 @@ class arrayVisitor = object end let annotateArrays loopBody = ignore @@ visitCilBlock (new arrayVisitor) loopBody -(*unroll loops that handle locks, threads and mallocs, asserts and reach_error*) -class loopUnrollingCallVisitor = object - inherit nopCilVisitor - - method! vinst = function - | Call (_,Lval ((Var info), NoOffset),args,_,_) when LibraryFunctions.is_special info -> ( - Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo info) @@ fun () -> - let desc = LibraryFunctions.find info in - match desc.special args with - | Malloc _ - | Calloc _ - | Realloc _ - | Alloca _ - | Lock _ - | Unlock _ - | ThreadCreate _ - | Assert _ - | Bounded _ - | ThreadJoin _ -> - raise Found; - | _ -> - if List.mem "specification" @@ get_string_list "ana.autotune.activated" && get_string "ana.specification" <> "" then ( - if Svcomp.is_error_function' info (SvcompSpec.of_option ()) then - raise Found - ); - DoChildren - ) - | _ -> DoChildren - -end - let loop_unrolling_factor loopStatement func totalLoops = let configFactor = get_int "exp.unrolling-factor" in if AutoTune0.isActivated "loopUnrollHeuristic" then - let unrollFunctionCalled = try - let thisVisitor = new loopUnrollingCallVisitor in - ignore (visitCilStmt thisVisitor loopStatement); - false; - with - Found -> true - in - (*unroll up to near an instruction count, higher if the loop uses malloc/lock/threads *) let loopStats = AutoTune0.collectFactors visitCilStmt loopStatement in if loopStats.instructions > 0 then match fixedLoopSize loopStatement func with From 42c3f5a4274fcd868a5255c1393c880df21af4d3 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Mon, 30 Sep 2024 17:21:51 +0300 Subject: [PATCH 405/566] Do not turn on autotune loop unrolling when the only specification is NoDataRace --- src/util/loopUnrolling.ml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index a191e414f9..aea2a2cd92 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -307,6 +307,11 @@ class arrayVisitor = object end let annotateArrays loopBody = ignore @@ visitCilBlock (new arrayVisitor) loopBody +let max_default_unrolls_per_spec (spec: Svcomp.Specification.t) = + match spec with + | NoDataRace -> 0 + | _ -> 4 + let loop_unrolling_factor loopStatement func totalLoops = let configFactor = get_int "exp.unrolling-factor" in if AutoTune0.isActivated "loopUnrollHeuristic" then @@ -314,7 +319,10 @@ let loop_unrolling_factor loopStatement func totalLoops = if loopStats.instructions > 0 then match fixedLoopSize loopStatement func with | Some i when i <= 20 -> Logs.debug "fixed loop size %d" i; i - | _ -> 4 + | _ -> + match Svcomp.Specification.of_option () with + | [] -> 4 + | specs -> BatList.max @@ List.map max_default_unrolls_per_spec specs else (* Don't unroll empty (= while(1){}) loops*) 0 From c341c1cfbaaad52ff1282b08661e85334f383f9e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Oct 2024 11:24:53 +0300 Subject: [PATCH 406/566] Add errors when architecture machdep not available --- src/common/util/cilfacade.ml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index a71f2544e3..344d29d246 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -50,10 +50,16 @@ let init_options () = Cabs2cil.addNestedScopeAttr := get_bool "cil.addNestedScopeAttr"; if get_bool "ana.sv-comp.enabled" then ( - Cil.envMachine := match get_string "exp.architecture" with + let machine = match get_string "exp.architecture" with | "32bit" -> Machdep.gcc32 | "64bit" -> Machdep.gcc64 | _ -> assert false + in + match machine with + | Some _ -> Cil.envMachine := machine + | None -> + GobRef.wrap AnalysisState.should_warn true (fun () -> Messages.msg_final Error ~category:Unsound "Machine definition not available for selected architecture"); + Logs.error "Machine definition not available for selected architecture, defaulting to host" ) let init () = From 6d04b1ad5e4fc22db4a9df46bbfdbcd114c71124 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Oct 2024 11:42:47 +0300 Subject: [PATCH 407/566] Disable 29-svcomp/36-svcomp-arch cram test on MacOS --- tests/regression/29-svcomp/dune | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/regression/29-svcomp/dune b/tests/regression/29-svcomp/dune index 2aede4e50b..95ac66a5ec 100644 --- a/tests/regression/29-svcomp/dune +++ b/tests/regression/29-svcomp/dune @@ -14,3 +14,7 @@ (cram (deps (glob_files *.c))) + +(cram + (applies_to 36-svcomp-arch) + (enabled_if (<> %{system} macosx))) ; https://dune.readthedocs.io/en/stable/reference/boolean-language.html From 5ba3996c496dca577cfeacc3a90f7872b81679c6 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 2 Oct 2024 11:47:02 +0300 Subject: [PATCH 408/566] Default to unrolling 2 times for no-overflow spec --- src/util/loopUnrolling.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index aea2a2cd92..c883e121fc 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -310,6 +310,7 @@ let annotateArrays loopBody = ignore @@ visitCilBlock (new arrayVisitor) loopBod let max_default_unrolls_per_spec (spec: Svcomp.Specification.t) = match spec with | NoDataRace -> 0 + | NoOverflow -> 2 | _ -> 4 let loop_unrolling_factor loopStatement func totalLoops = From 8b35075f3f386e78111a18dc7cbbb7ca23858df9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Oct 2024 16:51:56 +0300 Subject: [PATCH 409/566] Remove IntDomain.Booleans --- src/analyses/threadReturn.ml | 2 +- src/cdomain/value/cdomains/intDomain.ml | 6 ------ src/cdomain/value/cdomains/intDomain.mli | 4 ---- src/cdomains/lockDomain.ml | 2 +- .../00-sanity/33-hoare-over-paths.t | 20 +++++++++---------- tests/unit/maindomaintest.ml | 3 --- 6 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/analyses/threadReturn.ml b/src/analyses/threadReturn.ml index f3b9622b00..d72e2586e8 100644 --- a/src/analyses/threadReturn.ml +++ b/src/analyses/threadReturn.ml @@ -12,7 +12,7 @@ struct include Analyses.IdentitySpec let name () = "threadreturn" - module D = IntDomain.Booleans + module D = BoolDomain.MayBool include Analyses.ValueContexts(D) (* transfer functions *) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index a0e9a38f37..1e84e8365d 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -2436,12 +2436,6 @@ struct let invariant _ _ = Invariant.none (* TODO *) end -module Booleans = MakeBooleans ( - struct - let truename = "True" - let falsename = "False" - end) - (* Inclusion/Exclusion sets. Go to top on arithmetic operations (except for some easy cases, e.g. multiplication with 0). Joins on widen, i.e. precise integers as long as not derived from arithmetic expressions. *) module Enums : S with type int_t = Z.t = struct module R = Interval32 (* range for exclusion *) diff --git a/src/cdomain/value/cdomains/intDomain.mli b/src/cdomain/value/cdomains/intDomain.mli index ca64692290..74588b94d8 100644 --- a/src/cdomain/value/cdomains/intDomain.mli +++ b/src/cdomain/value/cdomains/intDomain.mli @@ -452,10 +452,6 @@ end module MakeBooleans (Names: BooleansNames): IkindUnawareS with type t = bool (** Creates an abstract domain for integers represented by boolean values. *) -module Booleans: IkindUnawareS with type t = bool -(** Boolean abstract domain, where true is output "True" and false is output - * "False" *) - (* module None: S with type t = unit (** Domain with nothing in it. *) diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 08353f4795..b71573d6f6 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -42,7 +42,7 @@ struct end (* true means exclusive lock and false represents reader lock*) -module RW = IntDomain.Booleans +module RW = BoolDomain.MayBool (* TODO: name booleans? *) (* pair Addr and RW; also change pretty printing*) module MakeRW (P: Printable.S) = diff --git a/tests/regression/00-sanity/33-hoare-over-paths.t b/tests/regression/00-sanity/33-hoare-over-paths.t index 5148f5e1f2..9f88f836b0 100644 --- a/tests/regression/00-sanity/33-hoare-over-paths.t +++ b/tests/regression/00-sanity/33-hoare-over-paths.t @@ -21,7 +21,7 @@ }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, - threadreturn:True, + threadreturn:true, escape:{}, mutexEvents:(), access:(), @@ -44,7 +44,7 @@ }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, - threadreturn:True, + threadreturn:true, escape:{}, mutexEvents:(), access:(), @@ -66,7 +66,7 @@ }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, - threadreturn:True, + threadreturn:true, escape:{}, mutexEvents:(), access:(), @@ -88,7 +88,7 @@ }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, - threadreturn:True, + threadreturn:true, escape:{}, mutexEvents:(), access:(), @@ -110,7 +110,7 @@ }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, - threadreturn:True, + threadreturn:true, escape:{}, mutexEvents:(), access:(), @@ -132,7 +132,7 @@ }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, - threadreturn:True, + threadreturn:true, escape:{}, mutexEvents:(), access:(), @@ -153,7 +153,7 @@ }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, - threadreturn:True, + threadreturn:true, escape:{}, mutexEvents:(), access:(), @@ -172,7 +172,7 @@ }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, - threadreturn:True, + threadreturn:true, escape:{}, mutexEvents:(), access:(), @@ -194,7 +194,7 @@ }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, - threadreturn:True, + threadreturn:true, escape:{}, mutexEvents:(), access:(), @@ -215,7 +215,7 @@ }, {}, {}, {}), threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), threadflag:Singlethreaded, - threadreturn:True, + threadreturn:true, escape:{}, mutexEvents:(), access:(), diff --git a/tests/unit/maindomaintest.ml b/tests/unit/maindomaintest.ml index 8e1db76b83..4b379a252f 100644 --- a/tests/unit/maindomaintest.ml +++ b/tests/unit/maindomaintest.ml @@ -28,9 +28,6 @@ let domains: (module Lattice.S) list = [ (* (module IntDomainProperties.IntegerSet); (* TODO: top properties error *) *) (module IntDomain.Lifted); (* not abstraction of IntegerSet *) - (* TODO: move to intDomains if passing *) - (module IntDomain.Booleans); - (* TODO: fix *) (* (module IntDomain.Enums); *) (* (module IntDomain.IntDomTuple); *) From f4cc7503d47ae45e2076832150454289a8c28e1b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Oct 2024 17:04:09 +0300 Subject: [PATCH 410/566] Remove Basetype.RawBools --- src/cdomain/value/cdomains/valueDomain.ml | 2 +- src/common/cdomains/basetype.ml | 11 ----------- src/domain/boolDomain.ml | 19 +++++++++++++------ 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index 0fbfb50955..f9f4b06ffb 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -69,7 +69,7 @@ end (* ZeroInit is false if malloc was used to allocate memory and true if calloc was used *) module ZeroInit : ZeroInit = struct - include Lattice.Fake(Basetype.RawBools) + include Lattice.Fake (BoolDomain.Bool) let name () = "zeroinit" let is_malloc x = not x diff --git a/src/common/cdomains/basetype.ml b/src/common/cdomains/basetype.ml index 1b846309aa..bf832b1c3c 100644 --- a/src/common/cdomains/basetype.ml +++ b/src/common/cdomains/basetype.ml @@ -33,17 +33,6 @@ struct let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape (show x)) end -module RawBools: Printable.S with type t = bool = -struct - include Printable.StdLeaf - open Pretty - type t = bool [@@deriving eq, ord, hash, to_yojson] - let show (x:t) = if x then "true" else "false" - let pretty () x = text (show x) - let name () = "raw bools" - let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) -end - module CilExp = struct include CilType.Exp diff --git a/src/domain/boolDomain.ml b/src/domain/boolDomain.ml index d92d716d7a..90337f1879 100644 --- a/src/domain/boolDomain.ml +++ b/src/domain/boolDomain.ml @@ -2,13 +2,20 @@ module Bool = struct - include Basetype.RawBools - (* type t = bool - let equal = Bool.equal - let compare = Bool.compare - let relift x = x - let arbitrary () = QCheck.bool *) + include Printable.StdLeaf + type t = bool [@@deriving eq, ord, hash] + let name () = "bool" + let show x = if x then "true" else "false" + include Printable.SimpleShow (struct + type nonrec t = t + let show = show + end) + let to_yojson = [%to_yojson: bool] (* override to_yojson from SimpleShow *) + + let arbitrary () = QCheck.bool + + (* For Lattice.S *) let pretty_diff () (x,y) = GoblintCil.Pretty.dprintf "%s: %a not leq %a" (name ()) pretty x pretty y end From 798a3d8098e8bd35a07882c5ded9b727ae5628d2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Oct 2024 17:13:31 +0300 Subject: [PATCH 411/566] Remove IntDomain.MakeBooleans --- src/cdomain/value/cdomains/concDomain.ml | 18 +++---- src/cdomain/value/cdomains/intDomain.ml | 60 ------------------------ src/cdomain/value/cdomains/intDomain.mli | 20 -------- src/cdomains/threadFlagDomain.ml | 7 +-- src/domain/boolDomain.ml | 39 ++++++++++++--- 5 files changed, 47 insertions(+), 97 deletions(-) diff --git a/src/cdomain/value/cdomains/concDomain.ml b/src/cdomain/value/cdomains/concDomain.ml index 5f609a31d8..467159c9da 100644 --- a/src/cdomain/value/cdomains/concDomain.ml +++ b/src/cdomain/value/cdomains/concDomain.ml @@ -1,7 +1,7 @@ (** Domains for thread sets and their uniqueness. *) -module ThreadSet = -struct +module ThreadSet = +struct include SetDomain.Make (ThreadIdDomain.Thread) let is_top = mem UnknownThread @@ -27,10 +27,11 @@ module CreatedThreadSet = ThreadSet module ThreadCreation = struct module UNames = struct - let truename = "repeated" - let falsename = "unique" + let name = "unique" + let true_name = "repeated" + let false_name = "unique" end - module Uniqueness = IntDomain.MakeBooleans (UNames) + module Uniqueness = BoolDomain.MakeMayBool (UNames) module ParentThreadSet = struct include ThreadSet @@ -38,12 +39,13 @@ struct end module DirtyExitNames = struct - let truename = "dirty exit" - let falsename = "clean exit" + let name = "exit" + let true_name = "dirty exit" + let false_name = "clean exit" end (* A thread exits cleanly iff it joined all threads it started, and they also all exit cleanly *) - module DirtyExit = IntDomain.MakeBooleans (DirtyExitNames) + module DirtyExit = BoolDomain.MakeMayBool (DirtyExitNames) include Lattice.Prod3 (Uniqueness) (ParentThreadSet) (DirtyExit) end diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index 1e84e8365d..e50b3f26cc 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -2376,66 +2376,6 @@ struct let project ik p t = t end -(* BOOLEAN DOMAINS *) - -module type BooleansNames = -sig - val truename: string - val falsename: string -end - -module MakeBooleans (N: BooleansNames) = -struct - type int_t = IntOps.Int64Ops.t - type t = bool [@@deriving eq, ord, hash, to_yojson] - let name () = "booleans" - let top () = true - let bot () = false - let top_of ik = top () - let bot_of ik = bot () - let show x = if x then N.truename else N.falsename - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - let is_top x = x (* override Std *) - - let equal_to i x = if x then `Top else failwith "unsupported: equal_to with bottom" - let cast_to ?(suppress_ovwarn=false) ?torg _ x = x (* ok since there's no smaller ikind to cast to *) - - let leq x y = not x || y - let join = (||) - let widen = join - let meet = (&&) - let narrow = meet - - let of_bool x = x - let to_bool x = Some x - let of_int x = x = Int64.zero - let to_int x = if x then None else Some Int64.zero - - let neg x = x - let add x y = x || y - let sub x y = x || y - let mul x y = x && y - let div x y = true - let rem x y = true - let lt n1 n2 = true - let gt n1 n2 = true - let le n1 n2 = true - let ge n1 n2 = true - let eq n1 n2 = true - let ne n1 n2 = true - let lognot x = true - let logand x y = x && y - let logor x y = x || y - let logxor x y = x && not y || not x && y - let shift_left n1 n2 = n1 - let shift_right n1 n2 = n1 - let c_lognot = (not) - let c_logand = (&&) - let c_logor = (||) - let arbitrary () = QCheck.bool - let invariant _ _ = Invariant.none (* TODO *) -end - (* Inclusion/Exclusion sets. Go to top on arithmetic operations (except for some easy cases, e.g. multiplication with 0). Joins on widen, i.e. precise integers as long as not derived from arithmetic expressions. *) module Enums : S with type int_t = Z.t = struct module R = Interval32 (* range for exclusion *) diff --git a/src/cdomain/value/cdomains/intDomain.mli b/src/cdomain/value/cdomains/intDomain.mli index 74588b94d8..e7667c9b14 100644 --- a/src/cdomain/value/cdomains/intDomain.mli +++ b/src/cdomain/value/cdomains/intDomain.mli @@ -436,23 +436,3 @@ module Reverse (Base: IkindUnawareS): IkindUnawareS with type t = Base.t and typ (* module IncExcInterval : S with type t = [ | `Excluded of Interval.t| `Included of Interval.t ] *) (** Inclusive and exclusive intervals. Warning: NOT A LATTICE *) module Enums : S with type int_t = Z.t - -(** {b Boolean domains} *) - -module type BooleansNames = -sig - val truename: string - (** The name of the [true] abstract value *) - - val falsename: string - (** The name of the [false] abstract value *) -end -(** Parameter signature for the [MakeBooleans] functor. *) - -module MakeBooleans (Names: BooleansNames): IkindUnawareS with type t = bool -(** Creates an abstract domain for integers represented by boolean values. *) - -(* -module None: S with type t = unit -(** Domain with nothing in it. *) -*) diff --git a/src/cdomains/threadFlagDomain.ml b/src/cdomains/threadFlagDomain.ml index 80ba9b7a52..42571656e7 100644 --- a/src/cdomains/threadFlagDomain.ml +++ b/src/cdomains/threadFlagDomain.ml @@ -15,10 +15,11 @@ module Trivial: S = struct module TrivialNames = struct - let truename = "Multithreaded" - let falsename = "Singlethreaded" + let name = "MT mode" + let true_name = "Multithreaded" + let false_name = "Singlethreaded" end - include IntDomain.MakeBooleans (TrivialNames) + include BoolDomain.MakeMayBool (TrivialNames) let is_multi x = x let is_not_main x = x diff --git a/src/domain/boolDomain.ml b/src/domain/boolDomain.ml index 90337f1879..18399365a5 100644 --- a/src/domain/boolDomain.ml +++ b/src/domain/boolDomain.ml @@ -1,17 +1,23 @@ (** Boolean domains. *) -module Bool = +module type Names = +sig + val name: string + val true_name: string + val false_name: string +end + +module MakeBool (Names: Names) = struct include Printable.StdLeaf type t = bool [@@deriving eq, ord, hash] - let name () = "bool" + let name () = Names.name - let show x = if x then "true" else "false" + let show x = if x then Names.true_name else Names.false_name include Printable.SimpleShow (struct type nonrec t = t let show = show end) - let to_yojson = [%to_yojson: bool] (* override to_yojson from SimpleShow *) let arbitrary () = QCheck.bool @@ -19,9 +25,22 @@ struct let pretty_diff () (x,y) = GoblintCil.Pretty.dprintf "%s: %a not leq %a" (name ()) pretty x pretty y end -module MayBool: Lattice.S with type t = bool = +module StdNames: Names = struct - include Bool + let name = "bool" + let true_name = "true" + let false_name = "false" +end + +module Bool = +struct + include MakeBool (StdNames) + let to_yojson = [%to_yojson: bool] (* override to_yojson from SimpleShow *) +end + +module MakeMayBool (Names: Names): Lattice.S with type t = bool = +struct + include MakeBool (Names) let bot () = false let is_bot x = x = false let top () = true @@ -33,6 +52,14 @@ struct let narrow = (&&) end +module MayBool: Lattice.S with type t = bool = +struct + include MakeMayBool (StdNames) + let to_yojson = [%to_yojson: bool] (* override to_yojson from SimpleShow *) +end + +(* TODO: MakeMustBool? *) + module MustBool: Lattice.S with type t = bool = struct include Bool From 09e02ce53d91a454badbd030dca43e04e802903c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 2 Oct 2024 17:14:18 +0300 Subject: [PATCH 412/566] Remove unused opens in ContextGasLifter --- src/lifters/contextGasLifter.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lifters/contextGasLifter.ml b/src/lifters/contextGasLifter.ml index adb55aa7a2..98974d81a1 100644 --- a/src/lifters/contextGasLifter.ml +++ b/src/lifters/contextGasLifter.ml @@ -3,9 +3,7 @@ open Batteries open GoblintCil -open MyCFG open Analyses -open ConstrSys open GobConfig module M = Messages From 25a1aca3e559e954bd45bfdeaa932eefe7728e9e Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 20 Jun 2024 13:01:59 +0300 Subject: [PATCH 413/566] Assume var is 0 if there was no assign before loop --- src/util/loopUnrolling.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index c883e121fc..7347bca361 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -277,7 +277,8 @@ let fixedLoopSize loopStatement func = if getsPointedAt var func then None else - let* start = constBefore var loopStatement func in + (* Assume var value to be 0 if there was no constant assignment to the var before loop *) + let start = Option.value (constBefore var loopStatement func) ~default:Z.zero in let* diff = assignmentDifference (loopBody loopStatement) var in let* goal = adjustGoal diff goal op in let iterations = loopIterations start diff goal (op=Ne) in From 64b3baaec718a08698c505c052dee1c50e19bfdb Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 27 Sep 2024 15:12:01 +0300 Subject: [PATCH 414/566] Bugfix: do not assume loop start to be 0 in case of decreasing values --- src/util/loopUnrolling.ml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 7347bca361..905cc39e6a 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -277,9 +277,10 @@ let fixedLoopSize loopStatement func = if getsPointedAt var func then None else - (* Assume var value to be 0 if there was no constant assignment to the var before loop *) - let start = Option.value (constBefore var loopStatement func) ~default:Z.zero in - let* diff = assignmentDifference (loopBody loopStatement) var in + let diff = Option.value (assignmentDifference (loopBody loopStatement) var) ~default:Z.one in + (* Assume var start value if there was no constant assignment to the var before loop; + Assume it to be 0, if loop is increasing and 11 (TODO: can we do better than just 11?) if loop is decreasing *) + let start = Option.value (constBefore var loopStatement func) ~default:(if diff < Z.zero then Z.of_int 11 else Z.zero) in let* goal = adjustGoal diff goal op in let iterations = loopIterations start diff goal (op=Ne) in Logs.debug "comparison: %a" CilType.Exp.pretty comparison; From c439f7934ecc50a97a300b280aaa740187ee0aab Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 2 Oct 2024 22:18:05 +0300 Subject: [PATCH 415/566] Handle loop statement comparison between two variables in loopUnrolling --- src/util/loopUnrolling.ml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index c883e121fc..fe9fec6ce8 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -220,13 +220,21 @@ let findBreakComparison loopStatement = with WrongOrMultiple -> None -let getLoopVar = function +let getLoopVar loopStatement func = function | BinOp (op, (Const (CInt (goal, _, _) )), Lval ((Var varinfo), NoOffset), (TInt _)) when isCompare op && not varinfo.vglob -> (* TODO: define isCompare and flip in cilfacade and refactor to use instead of the many separately defined similar functions *) let flip = function | Lt -> Gt | Gt -> Lt | Ge -> Le | Le -> Ge | s -> s in Some (flip op, varinfo, goal) | BinOp (op, Lval ((Var varinfo), NoOffset), (Const (CInt (goal, _, _) )), (TInt _)) when isCompare op && not varinfo.vglob -> Some (op, varinfo, goal) + (* When loop condition has a comparison between variables, we assume that the left one is the counter and right one is the bound. + TODO: can we do something more meaningful instead of this assumption? *) + | BinOp (op, Lval ((Var varinfo), NoOffset), Lval ((Var varinfo2), NoOffset), (TInt _)) + | BinOp (op, CastE ((TInt _), (Lval ((Var varinfo), NoOffset))), Lval ((Var varinfo2), NoOffset), (TInt _)) when isCompare op && not varinfo.vglob && not varinfo2.vglob -> + begin match constBefore varinfo2 loopStatement func with + | Some goal -> Logs.debug "const: %a %a" CilType.Varinfo.pretty varinfo2 GobZ.pretty goal; Some (op, varinfo, goal) + | None -> None + end; | _ -> None let getsPointedAt var func = @@ -273,7 +281,7 @@ let loopIterations start diff goal shouldBeExact = let fixedLoopSize loopStatement func = let open GobOption.Syntax in let* comparison = findBreakComparison loopStatement in - let* op, var, goal = getLoopVar comparison in + let* op, var, goal = getLoopVar loopStatement func comparison in if getsPointedAt var func then None else From e625b5604b212a210eac29fb86d7b55f6d3a3191 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 3 Oct 2024 23:09:06 +0300 Subject: [PATCH 416/566] Do not compute nr of instructions and unroll empty loops as unrolling and analyzing empty loops is cheaper than visiting loops for collecting the nr of the instructions --- src/util/loopUnrolling.ml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index c883e121fc..175493314f 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -316,17 +316,12 @@ let max_default_unrolls_per_spec (spec: Svcomp.Specification.t) = let loop_unrolling_factor loopStatement func totalLoops = let configFactor = get_int "exp.unrolling-factor" in if AutoTune0.isActivated "loopUnrollHeuristic" then - let loopStats = AutoTune0.collectFactors visitCilStmt loopStatement in - if loopStats.instructions > 0 then - match fixedLoopSize loopStatement func with - | Some i when i <= 20 -> Logs.debug "fixed loop size %d" i; i - | _ -> - match Svcomp.Specification.of_option () with - | [] -> 4 - | specs -> BatList.max @@ List.map max_default_unrolls_per_spec specs - else - (* Don't unroll empty (= while(1){}) loops*) - 0 + match fixedLoopSize loopStatement func with + | Some i when i <= 20 -> Logs.debug "fixed loop size %d" i; i + | _ -> + match Svcomp.Specification.of_option () with + | [] -> 4 + | specs -> BatList.max @@ List.map max_default_unrolls_per_spec specs else configFactor From 30a2ace5914bf496e1a81b72b146239daba41893 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 4 Oct 2024 10:23:23 +0300 Subject: [PATCH 417/566] Remove fixed malloc wrappers from svcomp conf --- conf/svcomp-validate.json | 21 --------------------- conf/svcomp.json | 21 --------------------- 2 files changed, 42 deletions(-) diff --git a/conf/svcomp-validate.json b/conf/svcomp-validate.json index a234aeb0d5..8e11fee7f5 100644 --- a/conf/svcomp-validate.json +++ b/conf/svcomp-validate.json @@ -46,27 +46,6 @@ "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", - "ldv_kzalloc" - ] - }, "base": { "arrays": { "domain": "partitioned" diff --git a/conf/svcomp.json b/conf/svcomp.json index 467d294bdd..12740a00ce 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -45,27 +45,6 @@ "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", - "ldv_kzalloc" - ] - }, "base": { "arrays": { "domain": "partitioned" From 50b0bddc78b07ca5bd9d6712f20fd9f9a5b78e51 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 4 Oct 2024 11:02:30 +0300 Subject: [PATCH 418/566] Remove witness exclude-vars from svcomp conf --- conf/svcomp.json | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index 467d294bdd..682d524105 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -128,17 +128,7 @@ "after-lock": false, "other": false, "accessed": false, - "exact": true, - "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]+" - ] + "exact": true } }, "pre": { From 761282ba8f6b6d1d76df21097200dabf41edb65b Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Fri, 4 Oct 2024 13:18:56 +0300 Subject: [PATCH 419/566] Do not unroll loops with a nesting higher than 3 --- src/util/loopUnrolling.ml | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 175493314f..8c812d598b 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -402,16 +402,19 @@ let copy_and_patch_labels break_target current_continue_target stmts = let patchLabelsVisitor = new patchLabelsGotosVisitor(StatementHashTable.find_opt gotos) in List.map (visitCilStmt patchLabelsVisitor) stmts' -class loopUnrollingVisitor(func, totalLoops) = object +class loopUnrollingVisitor (func, totalLoops) = object (* Labels are simply handled by giving them a fresh name. Jumps coming from outside will still always go to the original label! *) inherit nopCilVisitor - method! vstmt s = - let duplicate_and_rem_labels s = - match s.skind with - | Loop (b,loc, loc2, break , continue) -> - let factor = loop_unrolling_factor s func totalLoops in - if(factor > 0) then ( + val mutable nests = 0 + + method! vstmt stmt = + let duplicate_and_rem_labels stmt = + match stmt.skind with + | Loop (b, loc, loc2, break, continue) -> + nests <- nests - 1; Logs.debug "nests: %i" nests; + let factor = loop_unrolling_factor stmt func totalLoops in + if factor > 0 then ( Logs.info "unrolling loop at %a with factor %d" CilType.Location.pretty loc factor; annotateArrays b; (* top-level breaks should immediately go to the end of the loop, and not just break out of the current iteration *) @@ -423,11 +426,14 @@ class loopUnrollingVisitor(func, totalLoops) = object one_copy_stmts @ [current_continue_target] ) in - mkStmt (Block (mkBlock (List.flatten copies @ [s; break_target]))) - ) else s (*no change*) - | _ -> s + mkStmt (Block (mkBlock (List.flatten copies @ [stmt; break_target]))) + ) else stmt (*no change*) + | _ -> stmt in - ChangeDoChildrenPost(s, duplicate_and_rem_labels) + match stmt.skind with + | Loop _ when nests + 1 < 4 -> nests <- nests + 1; ChangeDoChildrenPost(stmt, duplicate_and_rem_labels) + | Loop _ -> SkipChildren + | _ -> ChangeDoChildrenPost(stmt, duplicate_and_rem_labels) end let unroll_loops fd totalLoops = From 67f8fe9195d3c6a96b01a0a5ceddf05a81fb1ff6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 9 Oct 2024 10:28:06 +0300 Subject: [PATCH 420/566] Add test for invariant_set widening tokens (issue #1299) --- .../56-witness/64-apron-unassume-set-tokens.c | 18 ++++++ .../64-apron-unassume-set-tokens.yml | 59 +++++++++++++++++++ tests/regression/56-witness/dune | 3 +- 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/regression/56-witness/64-apron-unassume-set-tokens.c create mode 100644 tests/regression/56-witness/64-apron-unassume-set-tokens.yml diff --git a/tests/regression/56-witness/64-apron-unassume-set-tokens.c b/tests/regression/56-witness/64-apron-unassume-set-tokens.c new file mode 100644 index 0000000000..75a6b5eee5 --- /dev/null +++ b/tests/regression/56-witness/64-apron-unassume-set-tokens.c @@ -0,0 +1,18 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 64-apron-unassume-set-tokens.yml --set ana.apron.domain polyhedra --enable ana.widen.tokens +#include +// Uses polyhedra instead of octagon such that widening tokens are actually needed by test instead of narrowing. +// Copied & extended from 56-witness/12-apron-unassume-branch. +int main() { + int i = 0; + while (i < 100) { + i++; + } + assert(i == 100); + + int j = 0; + while (j < 100) { + j++; + } + assert(j == 100); + return 0; +} diff --git a/tests/regression/56-witness/64-apron-unassume-set-tokens.yml b/tests/regression/56-witness/64-apron-unassume-set-tokens.yml new file mode 100644 index 0000000000..8411ed045f --- /dev/null +++ b/tests/regression/56-witness/64-apron-unassume-set-tokens.yml @@ -0,0 +1,59 @@ +- entry_type: invariant_set + 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'' + ''64-apron-unassume-set-tokens.c''' + task: + input_files: + - 64-apron-unassume-set-tokens.c + input_file_hashes: + 64-apron-unassume-set-tokens.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + content: + - invariant: + type: location_invariant + location: + file_name: 64-apron-unassume-set-tokens.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 8 + column: 3 + function: main + value: 99LL - (long long )i >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 64-apron-unassume-set-tokens.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 8 + column: 3 + function: main + value: (long long )i >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 64-apron-unassume-set-tokens.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 14 + column: 3 + function: main + value: 99LL - (long long )j >= 0LL + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 64-apron-unassume-set-tokens.c + file_hash: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + line: 14 + column: 3 + function: main + value: (long long )j >= 0LL + format: c_expression diff --git a/tests/regression/56-witness/dune b/tests/regression/56-witness/dune index 215e47deb2..f6694c60ec 100644 --- a/tests/regression/56-witness/dune +++ b/tests/regression/56-witness/dune @@ -21,7 +21,8 @@ (run %{update_suite} hh-ex3 -q) (run %{update_suite} bh-ex1-poly -q) (run %{update_suite} apron-unassume-precheck -q) - (run %{update_suite} apron-tracked-global-annot -q))))) + (run %{update_suite} apron-tracked-global-annot -q) + (run %{update_suite} apron-unassume-set-tokens -q))))) (cram (deps (glob_files *.c) (glob_files ??-*.yml))) From 7ec6b0578b6da2996114c8f9a60a75cb056fa231 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 8 Oct 2024 17:50:41 +0300 Subject: [PATCH 421/566] Add optional int indices to widening tokens --- src/analyses/apron/relationAnalysis.apron.ml | 4 ++-- src/analyses/base.ml | 4 ++-- src/analyses/unassumeAnalysis.ml | 12 ++++++------ src/domains/events.ml | 4 ++-- src/lifters/wideningTokens.ml | 3 +-- src/lifters/wideningTokens0.ml | 6 ++++++ 6 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 src/lifters/wideningTokens0.ml diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index da14dfff1d..f82bd37e33 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -701,7 +701,7 @@ struct Priv.escape ctx.node (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st escaped | Assert exp -> assert_fn ctx exp true - | Events.Unassume {exp = e; uuids} -> + | Events.Unassume {exp = e; tokens} -> let e_orig = e in let ask = Analyses.ask_of_ctx ctx in let e = replace_deref_exps ctx.ask e in @@ -737,7 +737,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 tokens) (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 1699108394..a5a9fc150e 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -3091,8 +3091,8 @@ struct set ~ctx ctx.local (eval_lv ~ctx ctx.local lval) (Cilfacade.typeOfLval lval) (Thread (ValueDomain.Threads.singleton tid)) | Events.Assert exp -> assert_fn ctx exp true - | Events.Unassume {exp; uuids} -> - Timing.wrap "base unassume" (unassume ctx exp) uuids + | Events.Unassume {exp; tokens} -> + Timing.wrap "base unassume" (unassume ctx exp) tokens | Events.Longjmped {lval} -> begin match lval with | Some lval -> diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 8f8892b8be..348215993b 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -29,7 +29,7 @@ struct type inv = { exp: Cil.exp; - uuid: string; + token: WideningTokens.Token.t; } let invs: inv NH.t = NH.create 100 @@ -101,7 +101,7 @@ struct match InvariantParser.parse_cil inv_parser ~check:false ~fundec ~loc inv_cabs with | Ok inv_exp -> M.debug ~category:Witness ~loc:msgLoc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; - NH.add invs n {exp = inv_exp; uuid} + NH.add invs n {exp = inv_exp; token = (uuid, None)} (* TODO: Some *) | 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 @@ -154,7 +154,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 {exp = inv_exp; uuid} + EH.add (NH.find pre_invs n) pre_exp {exp = inv_exp; token = (uuid, None)} (* TODO: Some *) | 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 @@ -262,9 +262,9 @@ struct M.info ~category:Witness "unassume invariant: %a" CilType.Exp.pretty e; if not !AnalysisState.postsolving then ( if not (GobConfig.get_bool "ana.unassume.precheck" && Queries.ID.to_bool (ctx.ask (EvalInt e)) = Some false) then ( - let uuids = x.uuid :: List.map (fun {uuid; _} -> uuid) xs in - ctx.emit (Unassume {exp = e; uuids}); - List.iter WideningTokens.add uuids + let tokens = x.token :: List.map (fun {token; _} -> token) xs in + ctx.emit (Unassume {exp = e; tokens}); + List.iter WideningTokens.add tokens ) ); ctx.local diff --git a/src/domains/events.ml b/src/domains/events.ml index b194847bac..b3054b8416 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -14,7 +14,7 @@ type t = | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) (* TODO: unused *) | UpdateExpSplit of exp (** Used by expsplit analysis to evaluate [exp] on post-state. *) | Assert of exp - | Unassume of {exp: CilType.Exp.t; uuids: string list} + | Unassume of {exp: CilType.Exp.t; tokens: WideningTokens0.Token.t list} | Longjmped of {lval: CilType.Lval.t option} (** Should event be emitted after transfer function raises [Deadcode]? *) @@ -45,5 +45,5 @@ let pretty () = function | Assign {lval; exp} -> dprintf "Assign {lval=%a, exp=%a}" CilType.Lval.pretty lval CilType.Exp.pretty exp | UpdateExpSplit exp -> dprintf "UpdateExpSplit %a" d_exp exp | Assert exp -> dprintf "Assert %a" d_exp exp - | Unassume {exp; uuids} -> dprintf "Unassume {exp=%a; uuids=%a}" d_exp exp (docList Pretty.text) uuids + | Unassume {exp; tokens} -> dprintf "Unassume {exp=%a; tokens=%a}" d_exp exp (d_list ", " WideningTokens0.Token.pretty) tokens | Longjmped {lval} -> dprintf "Longjmped {lval=%a}" (docOpt (CilType.Lval.pretty ())) lval diff --git a/src/lifters/wideningTokens.ml b/src/lifters/wideningTokens.ml index 41bb5d8477..4d60099d7e 100644 --- a/src/lifters/wideningTokens.ml +++ b/src/lifters/wideningTokens.ml @@ -6,8 +6,7 @@ @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. *) +include WideningTokens0 (** Widening token set. *) module TS = SetDomain.ToppedSet (Token) (struct let topname = "Top" end) diff --git a/src/lifters/wideningTokens0.ml b/src/lifters/wideningTokens0.ml new file mode 100644 index 0000000000..dcbf77424e --- /dev/null +++ b/src/lifters/wideningTokens0.ml @@ -0,0 +1,6 @@ +(** Widening token. *) +module Token = +struct + (* Change to variant type if need other tokens than witness UUIDs. *) + include Printable.Prod (Basetype.RawStrings) (Printable.Option (IntDomain.Integers (IntOps.NIntOps)) (struct let name = "None" end)) +end From 21c000c71bfae7e31fbc18d83d61a802dd854c03 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 8 Oct 2024 17:55:02 +0300 Subject: [PATCH 422/566] Add invariant_set index to widening token --- src/analyses/unassumeAnalysis.ml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 348215993b..6b5b495233 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -90,7 +90,7 @@ struct 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 unassume_nodes_invariant ~loc ~nodes ?i inv = let msgLoc: M.Location.t = CilLocation loc in match InvariantParser.parse_cabs inv with | Ok inv_cabs -> @@ -101,7 +101,7 @@ struct match InvariantParser.parse_cil inv_parser ~check:false ~fundec ~loc inv_cabs with | Ok inv_exp -> M.debug ~category:Witness ~loc:msgLoc "located invariant to %a: %a" Node.pretty n Cil.d_exp inv_exp; - NH.add invs n {exp = inv_exp; token = (uuid, None)} (* TODO: Some *) + NH.add invs n {exp = inv_exp; token = (uuid, i)} | 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 @@ -154,7 +154,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 {exp = inv_exp; token = (uuid, None)} (* TODO: Some *) + EH.add (NH.find pre_invs n) pre_exp {exp = inv_exp; token = (uuid, None)} | 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 @@ -189,42 +189,42 @@ struct let unassume_invariant_set (invariant_set: YamlWitnessType.InvariantSet.t) = - let unassume_location_invariant (location_invariant: YamlWitnessType.InvariantSet.LocationInvariant.t) = + let unassume_location_invariant ~i (location_invariant: YamlWitnessType.InvariantSet.LocationInvariant.t) = let loc = YamlWitness.loc_of_location location_invariant.location in let inv = location_invariant.value in let msgLoc: M.Location.t = CilLocation loc in match Locator.find_opt location_locator loc with | Some nodes -> - unassume_nodes_invariant ~loc ~nodes inv + unassume_nodes_invariant ~loc ~nodes ~i inv | None -> M.warn ~category:Witness ~loc:msgLoc "couldn't locate invariant: %s" inv in - let unassume_loop_invariant (loop_invariant: YamlWitnessType.InvariantSet.LoopInvariant.t) = + let unassume_loop_invariant ~i (loop_invariant: YamlWitnessType.InvariantSet.LoopInvariant.t) = let loc = YamlWitness.loc_of_location loop_invariant.location in let inv = loop_invariant.value in let msgLoc: M.Location.t = CilLocation loc in match Locator.find_opt loop_locator loc with | Some nodes -> - unassume_nodes_invariant ~loc ~nodes inv + unassume_nodes_invariant ~loc ~nodes ~i inv | None -> M.warn ~category:Witness ~loc:msgLoc "couldn't locate invariant: %s" inv in - let validate_invariant (invariant: YamlWitnessType.InvariantSet.Invariant.t) = + let validate_invariant i (invariant: YamlWitnessType.InvariantSet.Invariant.t) = let target_type = YamlWitnessType.InvariantSet.InvariantType.invariant_type invariant.invariant_type in match YamlWitness.invariant_type_enabled target_type, invariant.invariant_type with | true, LocationInvariant x -> - unassume_location_invariant x + unassume_location_invariant ~i x | true, LoopInvariant x -> - unassume_loop_invariant x + unassume_loop_invariant ~i x | false, (LocationInvariant _ | LoopInvariant _) -> M.info_noloc ~category:Witness "disabled invariant of type %s" target_type in - List.iter validate_invariant invariant_set.content + List.iteri validate_invariant invariant_set.content in match YamlWitness.entry_type_enabled target_type, entry.entry_type with From 57a044713a03cd28d199fb16cd4c9b332b31f32d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 9 Oct 2024 10:35:11 +0300 Subject: [PATCH 423/566] Rename widening token modules --- src/analyses/apron/relationAnalysis.apron.ml | 6 +++--- src/analyses/base.ml | 6 +++--- src/analyses/mCP.ml | 12 ++++++------ src/analyses/unassumeAnalysis.ml | 4 ++-- src/domains/events.ml | 4 ++-- src/framework/control.ml | 2 +- src/goblint_lib.ml | 3 ++- src/lifters/wideningToken.ml | 4 ++++ .../{wideningTokens.ml => wideningTokenLifter.ml} | 2 +- src/lifters/wideningTokens0.ml | 6 ------ 10 files changed, 24 insertions(+), 25 deletions(-) create mode 100644 src/lifters/wideningToken.ml rename src/lifters/{wideningTokens.ml => wideningTokenLifter.ml} (99%) delete mode 100644 src/lifters/wideningTokens0.ml diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index f82bd37e33..28e365bd97 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -690,7 +690,7 @@ struct Priv.lock ask ctx.global st m ) st addr | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) - WideningTokens.with_local_side_tokens (fun () -> + WideningTokenLifter.with_local_side_tokens (fun () -> CommonPriv.lift_unlock ask (fun st m -> Priv.unlock ask ctx.global ctx.sideg st m ) st addr @@ -737,7 +737,7 @@ struct (* TODO: parallel write_global? *) let st = - WideningTokens.with_side_tokens (WideningTokens.TS.of_list tokens) (fun () -> + WideningTokenLifter.with_side_tokens (WideningTokenLifter.TS.of_list tokens) (fun () -> VH.fold (fun v v_in st -> (* TODO: is this sideg fine? *) write_global ask ctx.global ctx.sideg st v v_in @@ -771,7 +771,7 @@ struct let new_value = RD.join old_value st in PCU.RH.replace results ctx.node new_value; end; - WideningTokens.with_local_side_tokens (fun () -> + WideningTokenLifter.with_local_side_tokens (fun () -> Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread]) ) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index a5a9fc150e..fcf720e5eb 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -447,7 +447,7 @@ struct in if M.tracing then M.tracel "sync" "sync multi=%B earlyglobs=%B" multi !earlyglobs; if !earlyglobs || multi then - WideningTokens.with_local_side_tokens (fun () -> + WideningTokenLifter.with_local_side_tokens (fun () -> Priv.sync (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) ctx.local reason ) else @@ -3058,7 +3058,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 () -> + WideningTokenLifter.with_side_tokens (WideningTokenLifter.TS.of_list uuids) (fun () -> CPA.fold (fun x v acc -> let addr: AD.t = AD.of_mval (x, `NoOffset) in set ~ctx ~invariant:false acc addr x.vtype v @@ -3077,7 +3077,7 @@ struct Priv.lock ask (priv_getg ctx.global) st m ) st addr | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) - WideningTokens.with_local_side_tokens (fun () -> + WideningTokenLifter.with_local_side_tokens (fun () -> CommonPriv.lift_unlock ask (fun st m -> Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st m ) st addr diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 6212b6de90..742e796fbd 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -156,20 +156,20 @@ struct else iter (uncurry spawn_one) @@ group_assoc_eq Basetype.Variables.equal xs - let do_sideg ctx (xs:(V.t * (WideningTokens.TS.t * G.t)) list) = + let do_sideg ctx (xs:(V.t * (WideningTokenLifter.TS.t * G.t)) list) = let side_one v dts = let side_one_ts ts d = (* 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; + let old_side_tokens = !WideningTokenLifter.side_tokens in + WideningTokenLifter.side_tokens := ts; Fun.protect (fun () -> ctx.sideg v @@ fold_left G.join (G.bot ()) d ) ~finally:(fun () -> - WideningTokens.side_tokens := old_side_tokens + WideningTokenLifter.side_tokens := old_side_tokens ) in - iter (uncurry side_one_ts) @@ group_assoc_eq WideningTokens.TS.equal dts + iter (uncurry side_one_ts) @@ group_assoc_eq WideningTokenLifter.TS.equal dts in iter (uncurry side_one) @@ group_assoc_eq V.equal xs @@ -355,7 +355,7 @@ struct | None -> (fun ?(multiple=false) v d -> failwith ("Cannot \"spawn\" in " ^ tfname ^ " context.")) in let sideg = match sides with - | Some sides -> (fun v g -> sides := (v, (!WideningTokens.side_tokens, g)) :: !sides) + | Some sides -> (fun v g -> sides := (v, (!WideningTokenLifter.side_tokens, g)) :: !sides) | None -> (fun v g -> failwith ("Cannot \"sideg\" in " ^ tfname ^ " context.")) in let emit = match emits with diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 6b5b495233..615dbd3266 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -29,7 +29,7 @@ struct type inv = { exp: Cil.exp; - token: WideningTokens.Token.t; + token: WideningToken.t; } let invs: inv NH.t = NH.create 100 @@ -264,7 +264,7 @@ struct if not (GobConfig.get_bool "ana.unassume.precheck" && Queries.ID.to_bool (ctx.ask (EvalInt e)) = Some false) then ( let tokens = x.token :: List.map (fun {token; _} -> token) xs in ctx.emit (Unassume {exp = e; tokens}); - List.iter WideningTokens.add tokens + List.iter WideningTokenLifter.add tokens ) ); ctx.local diff --git a/src/domains/events.ml b/src/domains/events.ml index b3054b8416..cf12900c98 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -14,7 +14,7 @@ type t = | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) (* TODO: unused *) | UpdateExpSplit of exp (** Used by expsplit analysis to evaluate [exp] on post-state. *) | Assert of exp - | Unassume of {exp: CilType.Exp.t; tokens: WideningTokens0.Token.t list} + | Unassume of {exp: CilType.Exp.t; tokens: WideningToken.t list} | Longjmped of {lval: CilType.Lval.t option} (** Should event be emitted after transfer function raises [Deadcode]? *) @@ -45,5 +45,5 @@ let pretty () = function | Assign {lval; exp} -> dprintf "Assign {lval=%a, exp=%a}" CilType.Lval.pretty lval CilType.Exp.pretty exp | UpdateExpSplit exp -> dprintf "UpdateExpSplit %a" d_exp exp | Assert exp -> dprintf "Assert %a" d_exp exp - | Unassume {exp; tokens} -> dprintf "Unassume {exp=%a; tokens=%a}" d_exp exp (d_list ", " WideningTokens0.Token.pretty) tokens + | Unassume {exp; tokens} -> dprintf "Unassume {exp=%a; tokens=%a}" d_exp exp (d_list ", " WideningToken.pretty) tokens | Longjmped {lval} -> dprintf "Longjmped {lval=%a}" (docOpt (CilType.Lval.pretty ())) lval diff --git a/src/framework/control.ml b/src/framework/control.ml index 1d0ebb869b..2566939817 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -39,7 +39,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 (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) + |> lift (get_bool "ana.widen.tokens") (module WideningTokenLifter.Lifter) |> lift true (module LongjmpLifter.Lifter) |> lift termination_enabled (module RecursionTermLifter.Lifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) ) diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 91f9837419..d8fd408151 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -180,7 +180,8 @@ module SpecLifters = SpecLifters module LongjmpLifter = LongjmpLifter module RecursionTermLifter = RecursionTermLifter module ContextGasLifter = ContextGasLifter -module WideningTokens = WideningTokens +module WideningToken = WideningToken +module WideningTokenLifter = WideningTokenLifter module WitnessConstraints = WitnessConstraints diff --git a/src/lifters/wideningToken.ml b/src/lifters/wideningToken.ml new file mode 100644 index 0000000000..d780c4e793 --- /dev/null +++ b/src/lifters/wideningToken.ml @@ -0,0 +1,4 @@ +(** Widening token for {!WideningTokenLifter}. *) + +(* Change to variant type if need other tokens than witness UUIDs. *) +include Printable.Prod (Basetype.RawStrings) (Printable.Option (IntDomain.Integers (IntOps.NIntOps)) (struct let name = "None" end)) diff --git a/src/lifters/wideningTokens.ml b/src/lifters/wideningTokenLifter.ml similarity index 99% rename from src/lifters/wideningTokens.ml rename to src/lifters/wideningTokenLifter.ml index 4d60099d7e..634468a9ca 100644 --- a/src/lifters/wideningTokens.ml +++ b/src/lifters/wideningTokenLifter.ml @@ -6,7 +6,7 @@ @see Mihaila, B., Sepp, A. & Simon, A. Widening as Abstract Domain. *) -include WideningTokens0 +module Token = WideningToken (** Widening token set. *) module TS = SetDomain.ToppedSet (Token) (struct let topname = "Top" end) diff --git a/src/lifters/wideningTokens0.ml b/src/lifters/wideningTokens0.ml deleted file mode 100644 index dcbf77424e..0000000000 --- a/src/lifters/wideningTokens0.ml +++ /dev/null @@ -1,6 +0,0 @@ -(** Widening token. *) -module Token = -struct - (* Change to variant type if need other tokens than witness UUIDs. *) - include Printable.Prod (Basetype.RawStrings) (Printable.Option (IntDomain.Integers (IntOps.NIntOps)) (struct let name = "None" end)) -end From a2817445e67768d30ef86b2ece90b5f00d3ffee5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 9 Oct 2024 10:38:14 +0300 Subject: [PATCH 424/566] Improve widening token output --- src/lifters/wideningToken.ml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/lifters/wideningToken.ml b/src/lifters/wideningToken.ml index d780c4e793..0639521038 100644 --- a/src/lifters/wideningToken.ml +++ b/src/lifters/wideningToken.ml @@ -1,4 +1,16 @@ (** Widening token for {!WideningTokenLifter}. *) +module Uuid = +struct + include Basetype.RawStrings + let name () = "uuid" +end + +module Index = +struct + include Printable.Option (IntDomain.Integers (IntOps.NIntOps)) (struct let name = "None" end) + let name () = "index" +end + (* Change to variant type if need other tokens than witness UUIDs. *) -include Printable.Prod (Basetype.RawStrings) (Printable.Option (IntDomain.Integers (IntOps.NIntOps)) (struct let name = "None" end)) +include Printable.Prod (Uuid) (Index) From a5bedf619a4c8aa42e0e64bc70d919748e23a1a4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 9 Oct 2024 10:56:50 +0300 Subject: [PATCH 425/566] Pin unreleased camlidl and apron for stability (issue #1520) --- goblint.opam | 4 ++++ goblint.opam.locked | 8 ++++++++ goblint.opam.template | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/goblint.opam b/goblint.opam index b1f1ee97d0..44e5ccd2c2 100644 --- a/goblint.opam +++ b/goblint.opam @@ -98,6 +98,10 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" ] + # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release + [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] + # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release + [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} diff --git a/goblint.opam.locked b/goblint.opam.locked index 97a8385312..5f01f7915a 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -142,6 +142,14 @@ pin-depends: [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" ] + [ + "camlidl.1.12" + "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" + ] + [ + "apron.v0.9.15" + "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" + ] ] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} description: """\ diff --git a/goblint.opam.template b/goblint.opam.template index a8a46aa108..0a517fbfa0 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -3,6 +3,10 @@ available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" ] + # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release + [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] + # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release + [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} From 7898bc572ac38e2f58e908acde02aadbb9268fc8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 9 Oct 2024 16:51:20 +0300 Subject: [PATCH 426/566] Remove old unused exceptions --- src/analyses/base.ml | 2 -- src/analyses/symbLocks.ml | 2 -- src/analyses/varEq.ml | 2 -- src/solver/topDown_deprecated.ml | 2 -- 4 files changed, 8 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 1699108394..e429155e4d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -34,8 +34,6 @@ module MainFunctor (Priv:BasePriv.S) (RVEval:BaseDomain.ExpEvaluator with type t struct include Analyses.DefaultSpec - exception Top - module Dom = BaseDomain.DomFunctor (Priv.D) (RVEval) type t = Dom.t module D = Dom diff --git a/src/analyses/symbLocks.ml b/src/analyses/symbLocks.ml index 0119f09288..ab116c525d 100644 --- a/src/analyses/symbLocks.ml +++ b/src/analyses/symbLocks.ml @@ -21,8 +21,6 @@ module Spec = struct include Analyses.DefaultSpec - exception Top - module D = SymbLocksDomain.Symbolic include Analyses.ValueContexts(D) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 8ece99d6e8..78013ec21d 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -11,8 +11,6 @@ open Analyses module Spec = struct - exception Top - include Analyses.DefaultSpec module D = diff --git a/src/solver/topDown_deprecated.ml b/src/solver/topDown_deprecated.ml index 16c45fcd16..a46da9e441 100644 --- a/src/solver/topDown_deprecated.ml +++ b/src/solver/topDown_deprecated.ml @@ -4,8 +4,6 @@ open Batteries open ConstrSys open Messages -exception SolverCannotDoGlobals - (** modified SLR3 as top down solver *) module TD3 = From 881532da16ecc8b9b8507f307c4ffdaa07421787 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 10 Oct 2024 12:00:23 +0300 Subject: [PATCH 427/566] Ignore labels inserted by loopunrolling when finding latest var assigns --- src/util/loopUnrolling.ml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index fe9fec6ce8..5739fd70e5 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -154,7 +154,17 @@ let constBefore var loop f = let targetLocation = loopLocation loop in let rec lastAssignmentToVarBeforeLoop (current: (Z.t option)) (statements: stmt list) = match statements with | st::stmts -> ( - let current' = if st.labels <> [] then (Logs.debug "has Label"; (None)) else current in + let current' = + (* If there exists labels that are not the ones inserted by loop unrolling, forget the found assigned constant value *) + if List.exists (function + | Label (s,_,_) -> not (String.starts_with ~prefix:"loop_continue" s || String.starts_with ~prefix:"loop_end" s) + | _ -> true) st.labels + then + (Logs.debug "has Label"; (None)) + else + current + in + (* let current' = current in *) match st.skind with | Instr list -> ( match lastAssignToVar var list with From 13bcf346fb15758de54947e13bfdd747e430d289 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 10 Oct 2024 12:02:13 +0300 Subject: [PATCH 428/566] Remove commented out code --- src/util/loopUnrolling.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 5739fd70e5..39e17d14d7 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -164,7 +164,6 @@ let constBefore var loop f = else current in - (* let current' = current in *) match st.skind with | Instr list -> ( match lastAssignToVar var list with From 3ed322d9c74e5e9f543828fa8af3121b20650df2 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 10 Oct 2024 12:46:27 +0300 Subject: [PATCH 429/566] Restore that empty loops are not unrolled but do it more efficiently --- src/util/loopUnrolling.ml | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 8c812d598b..dd34085d43 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -119,6 +119,20 @@ class findAssignmentConstDiff((diff: Z.t option ref), var) = object | _ -> SkipChildren end +class findStmtContainsInstructions = object + inherit nopCilVisitor + method! vinst = function + | Set _ + | Call _ -> raise Found + | _ -> DoChildren +end + +let containsInstructions stmt = + try + ignore @@ visitCilStmt (new findStmtContainsInstructions) stmt; false + with Found -> + true + let isCompare = function | Lt | Gt | Le | Ge | Ne -> true (*an loop that test for equality can not be of the type we look for*) | _ -> false @@ -315,15 +329,18 @@ let max_default_unrolls_per_spec (spec: Svcomp.Specification.t) = let loop_unrolling_factor loopStatement func totalLoops = let configFactor = get_int "exp.unrolling-factor" in - if AutoTune0.isActivated "loopUnrollHeuristic" then - match fixedLoopSize loopStatement func with - | Some i when i <= 20 -> Logs.debug "fixed loop size %d" i; i - | _ -> - match Svcomp.Specification.of_option () with - | [] -> 4 - | specs -> BatList.max @@ List.map max_default_unrolls_per_spec specs - else - configFactor + if containsInstructions loopStatement then + if AutoTune0.isActivated "loopUnrollHeuristic" then + match fixedLoopSize loopStatement func with + | Some i when i <= 20 -> Logs.debug "fixed loop size %d" i; i + | _ -> + match Svcomp.Specification.of_option () with + | [] -> 4 + | specs -> BatList.max @@ List.map max_default_unrolls_per_spec specs + else + configFactor + else (* Don't unroll empty (= while(1){}) loops*) + 0 (*actual loop unrolling*) From eb1fdedaff59992c966b8f5dfa2044891da1a220 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 10 Oct 2024 13:11:04 +0300 Subject: [PATCH 430/566] Cram tests: do not unroll empty loops even if unrolling factor is manually set --- tests/regression/55-loop-unrolling/08-bad.t | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/regression/55-loop-unrolling/08-bad.t b/tests/regression/55-loop-unrolling/08-bad.t index f49ad67b5b..a8d8d62522 100644 --- a/tests/regression/55-loop-unrolling/08-bad.t +++ b/tests/regression/55-loop-unrolling/08-bad.t @@ -1,6 +1,4 @@ $ goblint --set lib.activated '[]' --set exp.unrolling-factor 1 --enable justcil --set dbg.justcil-printer clean 08-bad.c - [Info] unrolling loop at 08-bad.c:9:7-9:23 with factor 1 - [Info] unrolling loop at 08-bad.c:15:8-15:24 with factor 1 int main(void) { int m ; @@ -8,11 +6,6 @@ { { goto switch_default; - { - if (! 0) { - goto loop_end; - } - loop_continue_0: /* CIL Label */ ; switch_default: /* CIL Label */ { while (1) { @@ -23,16 +16,9 @@ } while_break: /* CIL Label */ ; } - loop_end: /* CIL Label */ ; - } switch_break: /* CIL Label */ ; } goto lab; - { - if (! 0) { - goto loop_end___0; - } - loop_continue_0___0: /* CIL Label */ ; lab: { while (1) { @@ -43,8 +29,6 @@ } while_break___0: /* CIL Label */ ; } - loop_end___0: /* CIL Label */ ; - } return (0); } } From 8214807e0d4366fece13eff43c16c4c78ad84aab Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 11 Oct 2024 10:10:58 +0300 Subject: [PATCH 431/566] Replace most physical equality on immutable types It's implementation-defined behavior that differs between bytecode and native OCaml compilers. --- src/analyses/uninit.ml | 2 +- src/common/util/cilfacade.ml | 8 ++++---- src/domain/boolDomain.ml | 4 ++-- src/domain/lattice.ml | 10 +++++----- src/incremental/compareAST.ml | 2 +- src/incremental/compareCFG.ml | 2 +- src/incremental/compareCIL.ml | 2 +- src/util/loopUnrolling.ml | 4 ++-- src/witness/myARG.ml | 1 + 9 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index a385d0a1cd..8c217cda4e 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -135,7 +135,7 @@ struct in let utar, uoth = unrollType target, unrollType other in match ofs, utar, uoth with - | `NoOffset, _ , _ when utar == uoth -> [v, rev cx] + | `NoOffset, _ , _ when CilType.Typ.equal utar uoth -> [v, rev cx] | `NoOffset, _ , TComp (c2,_) when not c2.cstruct -> (* unroll other (union) *) List.concat (List.rev_map (fun oth_f -> get_pfx v (`Field (oth_f, cx)) ofs utar oth_f.ftype) c2.cfields) diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 344d29d246..6e86701858 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -474,8 +474,8 @@ let rec pretty_typsig_like_typ (nameOpt: Pretty.doc option) () ts = (* ignore the const attribute for arrays *) let a' = dropAttributes [ "pconst" ] a in let name' = - if a' == [] then name else - if nameOpt == None then printAttributes a' else + if a' = [] then name else + if nameOpt = None then printAttributes a' else text "(" ++ printAttributes a' ++ name ++ text ")" in pretty_typsig_like_typ @@ -488,8 +488,8 @@ let rec pretty_typsig_like_typ (nameOpt: Pretty.doc option) () ts = | TSFun (restyp, args, isvararg, a) -> let name' = - if a == [] then name else - if nameOpt == None then printAttributes a else + if a = [] then name else + if nameOpt = None then printAttributes a else text "(" ++ printAttributes a ++ name ++ text ")" in pretty_typsig_like_typ diff --git a/src/domain/boolDomain.ml b/src/domain/boolDomain.ml index 18399365a5..9298460c6a 100644 --- a/src/domain/boolDomain.ml +++ b/src/domain/boolDomain.ml @@ -45,7 +45,7 @@ struct let is_bot x = x = false let top () = true let is_top x = x = true - let leq x y = x == y || y + let leq x y = x = y || y let join = (||) let widen = (||) let meet = (&&) @@ -67,7 +67,7 @@ struct let is_bot x = x = true let top () = false let is_top x = x = false - let leq x y = x == y || x + let leq x y = x = y || x let join = (&&) let widen = (&&) let meet = (||) diff --git a/src/domain/lattice.ml b/src/domain/lattice.ml index f29cb8217d..37a4a2fef5 100644 --- a/src/domain/lattice.ml +++ b/src/domain/lattice.ml @@ -153,11 +153,11 @@ struct include Printable.HConsed (Base) let lift_f2 f x y = f (unlift x) (unlift y) - let narrow x y = if Arg.assume_idempotent && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.narrow x y) - let widen x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.widen x y) - let meet x y = if Arg.assume_idempotent && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.meet x y) - let join x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.join x y) - let leq x y = (x.BatHashcons.tag == y.BatHashcons.tag) || lift_f2 Base.leq x y + let narrow x y = if Arg.assume_idempotent && x.BatHashcons.tag = y.BatHashcons.tag then x else lift (lift_f2 Base.narrow x y) + let widen x y = if x.BatHashcons.tag = y.BatHashcons.tag then x else lift (lift_f2 Base.widen x y) + let meet x y = if Arg.assume_idempotent && x.BatHashcons.tag = y.BatHashcons.tag then x else lift (lift_f2 Base.meet x y) + let join x y = if x.BatHashcons.tag = y.BatHashcons.tag then x else lift (lift_f2 Base.join x y) + let leq x y = (x.BatHashcons.tag = y.BatHashcons.tag) || lift_f2 Base.leq x y let is_top = lift_f Base.is_top let is_bot = lift_f Base.is_bot let top () = lift (Base.top ()) diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index 5ac7a90706..59adfe00be 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -91,7 +91,7 @@ and eq_exp (a: exp) (b: exp) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ | AlignOf typ1, AlignOf typ2 -> eq_typ_acc typ1 typ2 ~rename_mapping ~acc | AlignOfE exp1, AlignOfE exp2 -> eq_exp exp1 exp2 ~rename_mapping ~acc | UnOp (op1, exp1, typ1), UnOp (op2, exp2, typ2) -> - ((op1 == op2), rename_mapping) &&>> eq_exp exp1 exp2 ~acc &&>> eq_typ_acc typ1 typ2 ~acc + (CilType.Unop.equal op1 op2, rename_mapping) &&>> eq_exp exp1 exp2 ~acc &&>> eq_typ_acc typ1 typ2 ~acc | BinOp (op1, left1, right1, typ1), BinOp (op2, left2, right2, typ2) -> (op1 = op2, rename_mapping) &&>> eq_exp left1 left2 ~acc &&>> eq_exp right1 right2 ~acc &&>> eq_typ_acc typ1 typ2 ~acc | CastE (typ1, exp1), CastE (typ2, exp2) -> eq_typ_acc typ1 typ2 ~rename_mapping ~acc &&>> eq_exp exp1 exp2 ~acc | AddrOf lv1, AddrOf lv2 -> eq_lval lv1 lv2 ~rename_mapping ~acc diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 84b120b8e3..6c314ef7c9 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -18,7 +18,7 @@ let (&&<>) (prev_result: bool * rename_mapping) f : bool * rename_mapping = let eq_node (x, fun1) (y, fun2) ~rename_mapping = let isPseudoReturn f sid = let pid = Cilfacade.get_pseudo_return_id f in - sid == pid in + sid = pid in match x,y with | Statement s1, Statement s2 -> let p1 = isPseudoReturn fun1 s1.sid in diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index ea22e02a56..837bd65589 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -19,7 +19,7 @@ let name_of_global_col gc = match gc.def with | None -> raise (Failure "empty global record") let compare_global_col gc1 gc2 = compare (name_of_global_col gc1) (name_of_global_col gc2) -let equal_name_global_col gc1 gc2 = compare_global_col gc1 gc2 == 0 +let equal_name_global_col gc1 gc2 = compare_global_col gc1 gc2 = 0 let get_varinfo gc = match gc.decls, gc.def with | _, Some (Var v) -> v diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 905cc39e6a..26aaaa79e1 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -68,7 +68,7 @@ class isPointedAtVisitor(var) = object inherit nopCilVisitor method! vexpr = function - | AddrOf (Var info, NoOffset) when info.vid == var.vid -> raise Found + | AddrOf (Var info, NoOffset) when CilType.Varinfo.equal info var -> raise Found | _ -> DoChildren end @@ -76,7 +76,7 @@ class hasAssignmentVisitor(var) = object inherit nopCilVisitor method! vinst = function - | Set ((Var info, NoOffset),_,_,_) when info.vid == var.vid -> raise Found + | Set ((Var info, NoOffset),_,_,_) when CilType.Varinfo.equal info var -> raise Found | _ -> SkipChildren end diff --git a/src/witness/myARG.ml b/src/witness/myARG.ml index a4ab524a0e..6273ecdbd5 100644 --- a/src/witness/myARG.ml +++ b/src/witness/myARG.ml @@ -283,6 +283,7 @@ let partition_if_next if_next_n = module UnCilLogicIntra (Arg: SIntraOpt): SIntraOpt = struct open Cil + (* TODO: questionable (=) and (==) use here *) let is_equiv_stmtkind sk1 sk2 = match sk1, sk2 with | Instr is1, Instr is2 -> GobList.equal (=) is1 is2 From 8f5a891bedf6d029aedf00c02907f16b580756c0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 11 Oct 2024 12:11:01 +0300 Subject: [PATCH 432/566] Add parentheses to BoolDomain.leq-s to make precedence explicit It was already correct, but not obvious without looking up OCaml operator precedence. --- src/domain/boolDomain.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domain/boolDomain.ml b/src/domain/boolDomain.ml index 9298460c6a..77fcf7e108 100644 --- a/src/domain/boolDomain.ml +++ b/src/domain/boolDomain.ml @@ -45,7 +45,7 @@ struct let is_bot x = x = false let top () = true let is_top x = x = true - let leq x y = x = y || y + let leq x y = (x = y) || y let join = (||) let widen = (||) let meet = (&&) @@ -67,7 +67,7 @@ struct let is_bot x = x = true let top () = false let is_top x = x = false - let leq x y = x = y || x + let leq x y = (x = y) || x let join = (&&) let widen = (&&) let meet = (||) From 412a7ab0aa571af5b6bf0e3e3e4b2e729f0a1084 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 11 Oct 2024 12:17:23 +0300 Subject: [PATCH 433/566] Use ubuntu 22.04 in GitHub Actions To avoid https://github.com/ocaml/setup-ocaml/issues/872. --- .github/workflows/coverage.yml | 4 ++-- .github/workflows/docs.yml | 2 +- .github/workflows/indentation.yml | 2 +- .github/workflows/locked.yml | 10 +++++----- .github/workflows/unlocked.yml | 14 +++++++------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7f6d5a1cfb..fd2c55b84e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used @@ -40,7 +40,7 @@ jobs: ocaml-compiler: ${{ matrix.ocaml-compiler }} - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install dependencies diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c5b414a741..0ada04e369 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used diff --git a/.github/workflows/indentation.yml b/.github/workflows/indentation.yml index 96ef5ee56a..1c788e7554 100644 --- a/.github/workflows/indentation.yml +++ b/.github/workflows/indentation.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: - 4.14.x diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 4f892ea419..16655bfdc7 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 - macos-13 ocaml-compiler: - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file @@ -42,7 +42,7 @@ jobs: ocaml-compiler: ${{ matrix.ocaml-compiler }} - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install dependencies @@ -70,7 +70,7 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used @@ -91,7 +91,7 @@ jobs: ocaml-compiler: ${{ matrix.ocaml-compiler }} - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install spin @@ -112,7 +112,7 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index 0c4433d0af..f3fe6cc558 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -15,7 +15,7 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 - macos-13 ocaml-compiler: - 5.2.x @@ -30,7 +30,7 @@ jobs: - false include: - - os: ubuntu-latest + - os: ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: 4.14.x z3: true - os: macos-latest @@ -52,7 +52,7 @@ jobs: ocaml-compiler: ${{ matrix.ocaml-compiler }} - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install dependencies @@ -90,7 +90,7 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 - macos-13 ocaml-compiler: - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file, downgrade deps step @@ -112,7 +112,7 @@ jobs: ocaml-compiler: ${{ matrix.ocaml-compiler }} - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install dependencies @@ -187,7 +187,7 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 - macos-13 ocaml-compiler: - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file @@ -204,7 +204,7 @@ jobs: ocaml-compiler: ${{ matrix.ocaml-compiler }} - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install Goblint with test From 82729353e53b05422a883b2a724e35fc87948379 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 14 Oct 2024 12:49:18 +0200 Subject: [PATCH 434/566] Add example where per function gas with maximum leads to non-termination --- .../80-context_gas/25-per-fun-nonterm.c | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/regression/80-context_gas/25-per-fun-nonterm.c diff --git a/tests/regression/80-context_gas/25-per-fun-nonterm.c b/tests/regression/80-context_gas/25-per-fun-nonterm.c new file mode 100644 index 0000000000..4c3871688f --- /dev/null +++ b/tests/regression/80-context_gas/25-per-fun-nonterm.c @@ -0,0 +1,29 @@ +//PARAM: --enable ana.int.interval_set --set ana.context.gas_value 3 --set ana.context.gas_scope function +// NOTIMEOUT +void h(int n) { + int x; + + if(x) { + return; + } + + g(n+1); + h(n+1); +} + +void g(int n) { + int x; + + if(x) { + return; + } + + g(n+1); + h(n+1); +} + +int main() +{ + g(0); + h(0); +} From ce1866b6e61472957c13aac891b523712c2e46ae Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 14 Oct 2024 13:06:13 +0200 Subject: [PATCH 435/566] Fix per fundec gas --- src/lifters/contextGasLifter.ml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lifters/contextGasLifter.ml b/src/lifters/contextGasLifter.ml index 98974d81a1..75bd9f7641 100644 --- a/src/lifters/contextGasLifter.ml +++ b/src/lifters/contextGasLifter.ml @@ -121,12 +121,15 @@ let get_gas_lifter () = (module ContextGasLifter(GlobalGas):Spec2Spec) else let module PerFunctionGas:Gas = struct - module G = GasChain - module M = MapDomain.MapTop_LiftBot(CilType.Fundec)(G) + (* The order is reversed here to ensure that the minimum is used *) + (* 5 join 4 = 4 *) + module G = Lattice.Reverse(GasChain) + (* Missing bindings are bot, i.e., have maximal gas for this function *) + module M = MapDomain.MapBot_LiftTop(CilType.Fundec)(G) let startgas () = M.empty () let is_exhausted f v = GobOption.exists (fun g -> g <= 0) (M.find_opt f v) (* v <= 0 *) let callee_gas f v = - let c = Option.default (G.top ()) (M.find_opt f v) in + let c = Option.default (G.bot ()) (M.find_opt f v) in M.add f (max 0 c-1) v let thread_gas f v = match Cilfacade.find_varinfo_fundec f with From f54ce558d917fc59beb83f8db3a4d428d1be83e6 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 15 Oct 2024 13:15:07 +0300 Subject: [PATCH 436/566] Handle casts in loop statements for unrolling --- src/util/loopUnrolling.ml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 9ad6eb87d5..6639df61cb 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -244,16 +244,23 @@ let findBreakComparison loopStatement = None let getLoopVar loopStatement func = function - | BinOp (op, (Const (CInt (goal, _, _) )), Lval ((Var varinfo), NoOffset), (TInt _)) when isCompare op && not varinfo.vglob -> + | BinOp (op, Const (CInt (goal, _, _)), Lval (Var varinfo, NoOffset), TInt _) + | BinOp (op, Const (CInt (goal, _, _)), CastE (TInt _, Lval (Var varinfo, NoOffset)), TInt _) + | BinOp (op, CastE (TInt _, Const (CInt (goal, _, _))), Lval (Var varinfo, NoOffset), TInt _) + | BinOp (op, CastE (TInt _, Const (CInt (goal, _, _))), CastE (TInt _, Lval (Var varinfo, NoOffset)), TInt _) when isCompare op && not varinfo.vglob -> (* TODO: define isCompare and flip in cilfacade and refactor to use instead of the many separately defined similar functions *) let flip = function | Lt -> Gt | Gt -> Lt | Ge -> Le | Le -> Ge | s -> s in Some (flip op, varinfo, goal) - | BinOp (op, Lval ((Var varinfo), NoOffset), (Const (CInt (goal, _, _) )), (TInt _)) when isCompare op && not varinfo.vglob -> + | BinOp (op, Lval (Var varinfo, NoOffset), Const (CInt (goal, _, _)), TInt _) + | BinOp (op, Lval (Var varinfo, NoOffset), CastE (TInt _, Const (CInt (goal, _, _))), TInt _) + | BinOp (op, CastE (TInt _, Lval (Var varinfo, NoOffset)), Const (CInt (goal, _, _)), TInt _) + | BinOp (op, CastE (TInt _, Lval (Var varinfo, NoOffset)), CastE (TInt _, Const (CInt (goal, _, _))), TInt _) when isCompare op && not varinfo.vglob -> Some (op, varinfo, goal) (* When loop condition has a comparison between variables, we assume that the left one is the counter and right one is the bound. TODO: can we do something more meaningful instead of this assumption? *) - | BinOp (op, Lval ((Var varinfo), NoOffset), Lval ((Var varinfo2), NoOffset), (TInt _)) - | BinOp (op, CastE ((TInt _), (Lval ((Var varinfo), NoOffset))), Lval ((Var varinfo2), NoOffset), (TInt _)) when isCompare op && not varinfo.vglob && not varinfo2.vglob -> + | BinOp (op, Lval (Var varinfo, NoOffset), Lval (Var varinfo2, NoOffset), TInt _) + | BinOp (op, Lval (Var varinfo, NoOffset), CastE (TInt _, Lval (Var varinfo2, NoOffset)), TInt _) + | BinOp (op, CastE (TInt _, Lval (Var varinfo, NoOffset)), Lval (Var varinfo2, NoOffset), TInt _) when isCompare op && not varinfo.vglob && not varinfo2.vglob -> begin match constBefore varinfo2 loopStatement func with | Some goal -> Logs.debug "const: %a %a" CilType.Varinfo.pretty varinfo2 GobZ.pretty goal; Some (op, varinfo, goal) | None -> None From 872b1597a3c96747dfce5b316210368b0189737e Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 15 Oct 2024 16:21:34 +0300 Subject: [PATCH 437/566] Refactor: Simplify pattern matching of casts by using Cil.stripCasts --- src/util/loopUnrolling.ml | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index 6639df61cb..506337ac1b 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -243,30 +243,27 @@ let findBreakComparison loopStatement = with WrongOrMultiple -> None -let getLoopVar loopStatement func = function - | BinOp (op, Const (CInt (goal, _, _)), Lval (Var varinfo, NoOffset), TInt _) - | BinOp (op, Const (CInt (goal, _, _)), CastE (TInt _, Lval (Var varinfo, NoOffset)), TInt _) - | BinOp (op, CastE (TInt _, Const (CInt (goal, _, _))), Lval (Var varinfo, NoOffset), TInt _) - | BinOp (op, CastE (TInt _, Const (CInt (goal, _, _))), CastE (TInt _, Lval (Var varinfo, NoOffset)), TInt _) when isCompare op && not varinfo.vglob -> +let findLoopVarAndGoal loopStatement func (op, exp1, exp2) = + match Cil.stripCasts exp1, Cil.stripCasts exp2 with + | Const (CInt (goal, _, _)), Lval (Var varinfo, NoOffset) when not varinfo.vglob -> (* TODO: define isCompare and flip in cilfacade and refactor to use instead of the many separately defined similar functions *) let flip = function | Lt -> Gt | Gt -> Lt | Ge -> Le | Le -> Ge | s -> s in Some (flip op, varinfo, goal) - | BinOp (op, Lval (Var varinfo, NoOffset), Const (CInt (goal, _, _)), TInt _) - | BinOp (op, Lval (Var varinfo, NoOffset), CastE (TInt _, Const (CInt (goal, _, _))), TInt _) - | BinOp (op, CastE (TInt _, Lval (Var varinfo, NoOffset)), Const (CInt (goal, _, _)), TInt _) - | BinOp (op, CastE (TInt _, Lval (Var varinfo, NoOffset)), CastE (TInt _, Const (CInt (goal, _, _))), TInt _) when isCompare op && not varinfo.vglob -> + | Lval (Var varinfo, NoOffset), Const (CInt (goal, _, _)) when not varinfo.vglob -> Some (op, varinfo, goal) - (* When loop condition has a comparison between variables, we assume that the left one is the counter and right one is the bound. - TODO: can we do something more meaningful instead of this assumption? *) - | BinOp (op, Lval (Var varinfo, NoOffset), Lval (Var varinfo2, NoOffset), TInt _) - | BinOp (op, Lval (Var varinfo, NoOffset), CastE (TInt _, Lval (Var varinfo2, NoOffset)), TInt _) - | BinOp (op, CastE (TInt _, Lval (Var varinfo, NoOffset)), Lval (Var varinfo2, NoOffset), TInt _) when isCompare op && not varinfo.vglob && not varinfo2.vglob -> + | Lval (Var varinfo, NoOffset), Lval (Var varinfo2, NoOffset) when not varinfo.vglob && not varinfo2.vglob -> + (* When loop condition has a comparison between variables, we assume that the left one is the counter and right one is the bound. + TODO: can we do something more meaningful instead of this assumption? *) begin match constBefore varinfo2 loopStatement func with | Some goal -> Logs.debug "const: %a %a" CilType.Varinfo.pretty varinfo2 GobZ.pretty goal; Some (op, varinfo, goal) | None -> None end; | _ -> None +let getLoopVar loopStatement func = function + | BinOp (op, exp1, exp2, TInt _) when isCompare op -> findLoopVarAndGoal loopStatement func (op, exp1, exp2) + | _ -> None + let getsPointedAt var func = try let visitor = new isPointedAtVisitor (var) in From 2f5b50fa9081abda073a33b393ef33c282c1ebc4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 16 Oct 2024 16:51:40 +0300 Subject: [PATCH 438/566] Revert "Add hacky imaxabs sqrt refine support" This reverts commit f9765da81d64a99f77c385835c6c0a5c3db419da. --- src/analyses/baseInvariant.ml | 3 +-- tests/regression/39-signed-overflows/12-imaxabs-sqrt.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index d5b65a95f4..51a27e19f8 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -785,8 +785,7 @@ struct | TFloat (fk, _), FLongDouble | TFloat (FDouble as fk, _), FDouble | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st - | TInt (ik, _), _ -> inv_exp (Int (FD.to_int ik c)) e st (* TODO: is this cast refinement correct? *) - | t, fk -> fallback (fun () -> Pretty.dprintf "CastE: incompatible types %a and %a" CilType.Typ.pretty t CilType.Fkind.pretty fk) st) + | _ -> fallback (fun () -> Pretty.text "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 diff --git a/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c b/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c index 46512aed21..b121645b27 100644 --- a/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c +++ b/tests/regression/39-signed-overflows/12-imaxabs-sqrt.c @@ -6,7 +6,7 @@ int main() { int64_t data; if (data > (-0x7fffffffffffffff - 1) && imaxabs((intmax_t)data) <= sqrtl(0x7fffffffffffffffLL)) { - int64_t result = data * data; // NOWARN + int64_t result = data * data; // TODO NOWARN } return 8; } From f7a5afa966d6dc4b62748fdb1738f2b2aef2f844 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 16 Oct 2024 17:39:07 +0300 Subject: [PATCH 439/566] Add 39-signed-overflows/13-imaxabs-macos test --- .../39-signed-overflows/13-imaxabs-macos.c | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/regression/39-signed-overflows/13-imaxabs-macos.c diff --git a/tests/regression/39-signed-overflows/13-imaxabs-macos.c b/tests/regression/39-signed-overflows/13-imaxabs-macos.c new file mode 100644 index 0000000000..745d5b74c4 --- /dev/null +++ b/tests/regression/39-signed-overflows/13-imaxabs-macos.c @@ -0,0 +1,25 @@ +//PARAM: --enable ana.int.interval --set ana.activated[+] tmpSpecial +// 39-signed-overflows/11-imaxabs, but with long long as int64_t instead (https://github.com/goblint/analyzer/pull/1519#issuecomment-2417032186). +#include +#include +#include +int main() { + long long data; + if (data > (-0x7fffffffffffffff - 1)) + { + if (imaxabs(data) < 100) + { + __goblint_check(data < 100); + __goblint_check(-100 < data); + long long result = data * data; // NOWARN + } + + if(imaxabs(data) <= 100) + { + __goblint_check(data <= 100); + __goblint_check(-100 <= data); + long long result = data * data; // NOWARN + } + } + return 8; +} From 62834684764e5e1bc88705f19c54fa22a0d35d64 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 16 Oct 2024 17:55:20 +0300 Subject: [PATCH 440/566] Unroll cast type in BaseInvariant --- src/analyses/baseInvariant.ml | 58 +++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 51a27e19f8..52f0888d3f 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -777,33 +777,37 @@ struct | _ -> assert false end | Const _ , _ -> st (* nothing to do *) - | CastE ((TFloat (_, _)), e), Float c -> - (match unrollType (Cilfacade.typeOf e), FD.get_fkind c with - | TFloat (FLongDouble as fk, _), FFloat - | TFloat (FDouble as fk, _), FFloat - | TFloat (FLongDouble as fk, _), FDouble - | TFloat (fk, _), FLongDouble - | TFloat (FDouble as fk, _), FDouble - | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st - | _ -> fallback (fun () -> Pretty.text "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 -> - (match unrollType (Cilfacade.typeOf e) with - | (TInt(ik_e, _) as t') - | (TEnum ({ekind = ik_e; _ }, _) as t') -> - if VD.is_dynamically_safe_cast t t' (Int i) then - (* let c' = ID.cast_to ik_e c in *) - (* Suppressing overflow warnings as this is not a computation that comes from the program *) - let res_range = (ID.cast_to ~suppress_ovwarn:true ik (ID.top_of ik_e)) in - let c' = ID.cast_to ik_e (ID.meet c res_range) 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" 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 - else - fallback (fun () -> Pretty.dprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st - | x -> fallback (fun () -> Pretty.dprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st) - | v -> fallback (fun () -> Pretty.dprintf "CastE: e did not evaluate to Int, but %a" VD.pretty v) st) + | CastE (t, e), c_typed -> + begin match Cil.unrollType t, c_typed with + | TFloat (_, _), Float c -> + (match unrollType (Cilfacade.typeOf e), FD.get_fkind c with + | TFloat (FLongDouble as fk, _), FFloat + | TFloat (FDouble as fk, _), FFloat + | TFloat (FLongDouble as fk, _), FDouble + | TFloat (fk, _), FLongDouble + | TFloat (FDouble as fk, _), FDouble + | TFloat (FFloat as fk, _), FFloat -> inv_exp (Float (FD.cast_to fk c)) e st + | _ -> fallback (fun () -> Pretty.text "CastE: incompatible types") st) + | (TInt (ik, _) as t), Int c + | (TEnum ({ekind = ik; _ }, _) as t), 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 -> + (match unrollType (Cilfacade.typeOf e) with + | (TInt(ik_e, _) as t') + | (TEnum ({ekind = ik_e; _ }, _) as t') -> + if VD.is_dynamically_safe_cast t t' (Int i) then + (* let c' = ID.cast_to ik_e c in *) + (* Suppressing overflow warnings as this is not a computation that comes from the program *) + let res_range = (ID.cast_to ~suppress_ovwarn:true ik (ID.top_of ik_e)) in + let c' = ID.cast_to ik_e (ID.meet c res_range) 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" 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 + else + fallback (fun () -> Pretty.dprintf "CastE: %a evaluates to %a which is bigger than the type it is cast to which is %a" d_plainexp e ID.pretty i CilType.Typ.pretty t) st + | x -> fallback (fun () -> Pretty.dprintf "CastE: e did evaluate to Int, but the type did not match %a" CilType.Typ.pretty t) st) + | v -> fallback (fun () -> Pretty.dprintf "CastE: e did not evaluate to Int, but %a" VD.pretty v) st) + | _, _ -> fallback (fun () -> Pretty.dprintf "CastE: %a not implemented" d_plainexp (CastE (t, e))) st + end | e, _ -> fallback (fun () -> Pretty.dprintf "%a not implemented" d_plainexp e) st in if eval_bool exp st = Some (not tv) then contra st (* we already know that the branch is dead *) From 732b69a8ea5a3002fa68491ea0b24c1dede49761 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 17 Oct 2024 15:17:35 +0300 Subject: [PATCH 441/566] Lock zarith 1.14 for better hash (issue #1594) --- goblint.opam.locked | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goblint.opam.locked b/goblint.opam.locked index 5f01f7915a..9fbee1e02b 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -110,7 +110,7 @@ depends: [ "uutf" {= "1.0.3" & with-doc} "yaml" {= "3.2.0"} "yojson" {= "2.2.1"} - "zarith" {= "1.13"} + "zarith" {= "1.14"} ] build: [ ["dune" "subst"] {dev} From e12d6df901069f353c7a2a9ff08dfd6130a6507b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 17 Oct 2024 15:26:28 +0300 Subject: [PATCH 442/566] Copy svcomp confs to svcomp25 --- conf/svcomp25-validate.json | 122 ++++++++++++++++++++++++++++++++++++ conf/svcomp25.json | 117 ++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 conf/svcomp25-validate.json create mode 100644 conf/svcomp25.json diff --git a/conf/svcomp25-validate.json b/conf/svcomp25-validate.json new file mode 100644 index 0000000000..f0e99057d1 --- /dev/null +++ b/conf/svcomp25-validate.json @@ -0,0 +1,122 @@ +{ + "ana": { + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "float": { + "interval": true, + "evaluate_math_functions": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "race", + "escape", + "expRelation", + "mhp", + "assert", + "var_eq", + "symb_locks", + "region", + "thread", + "threadJoins", + "abortUnless", + "unassume" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "memLeak", + "threadflag" + ], + "context": { + "widen": false + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "octagon", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" + ] + }, + "widen": { + "tokens": true + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": false + }, + "yaml": { + "enabled": false, + "strict": true, + "format-version": "2.0", + "entry-types": [ + "location_invariant", + "loop_invariant", + "invariant_set", + "violation_sequence" + ], + "invariant-types": [ + "location_invariant", + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": true, + "other": true + } + }, + "pre": { + "enabled": false + } +} diff --git a/conf/svcomp25.json b/conf/svcomp25.json new file mode 100644 index 0000000000..aa3f625da9 --- /dev/null +++ b/conf/svcomp25.json @@ -0,0 +1,117 @@ +{ + "ana": { + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "float": { + "interval": true, + "evaluate_math_functions": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "race", + "escape", + "expRelation", + "mhp", + "assert", + "var_eq", + "symb_locks", + "region", + "thread", + "threadJoins", + "abortUnless" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "memLeak", + "threadflag" + ], + "context": { + "widen": false + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "octagon", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" + ] + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": true, + "id": "enumerate", + "unknown": false + }, + "yaml": { + "enabled": true, + "format-version": "2.0", + "entry-types": [ + "invariant_set" + ], + "invariant-types": [ + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": false, + "other": false, + "accessed": false, + "exact": true + } + }, + "pre": { + "enabled": false + } +} From 6a973802a229367f7112637c0b37d5e979560a8d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 17 Oct 2024 15:28:42 +0300 Subject: [PATCH 443/566] Update sv-comp/archive.sh for 2025 --- scripts/sv-comp/archive.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/sv-comp/archive.sh b/scripts/sv-comp/archive.sh index 37fa2758d9..aefac8f769 100755 --- a/scripts/sv-comp/archive.sh +++ b/scripts/sv-comp/archive.sh @@ -4,7 +4,7 @@ make clean -git tag -m "SV-COMP 2024" svcomp24 +git tag -m "SV-COMP 2025" svcomp25 dune build --profile=release src/goblint.exe rm -f goblint @@ -32,8 +32,8 @@ zip goblint/scripts/sv-comp/goblint.zip \ goblint/lib/libboxD.so \ goblint/lib/libpolkaMPQ.so \ goblint/lib/LICENSE.APRON \ - goblint/conf/svcomp24.json \ - goblint/conf/svcomp24-validate.json \ + goblint/conf/svcomp25.json \ + goblint/conf/svcomp25-validate.json \ goblint/lib/libc/stub/include/assert.h \ goblint/lib/goblint/runtime/include/goblint.h \ goblint/lib/libc/stub/src/stdlib.c \ From d3c5d353cec4b9b875c5a3f12bc09647f4c03bcf Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Oct 2024 12:20:55 +0300 Subject: [PATCH 444/566] Document SV-COMP bench-defs MR --- docs/developer-guide/releasing.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index 7530d9ad20..aca0749eb9 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -77,6 +77,8 @@ This includes: git tag name, git tag message and zipped conf file. +5. Open MR with conf file name to the [bench-defs](https://gitlab.com/sosy-lab/sv-comp/bench-defs) repository. + ### For each prerun 1. Update opam pins: From eed1e27067c47b2c86015d6fb1402f23ee7ffa22 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Oct 2024 12:32:44 +0300 Subject: [PATCH 445/566] Improve flat string domain hash --- src/cdomain/value/cdomains/stringDomain.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cdomain/value/cdomains/stringDomain.ml b/src/cdomain/value/cdomains/stringDomain.ml index 2b968b0321..5ed704ce69 100644 --- a/src/cdomain/value/cdomains/stringDomain.ml +++ b/src/cdomain/value/cdomains/stringDomain.ml @@ -20,9 +20,10 @@ let reset_lazy () = type t = string option [@@deriving eq, ord, hash] +(** [None] means top. *) let hash x = - if get_string_domain () = Disjoint then + if get_string_domain () <> Unit then hash x else 13859 From 2284da8ed5f909c39f204f9b123c3ce3d9149fee Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 18 Oct 2024 12:36:26 +0300 Subject: [PATCH 446/566] Improve StringDomain type safety by matching all domains This is more robust against changes to the possible choices of domain. It would have avoided issue #1594. --- src/cdomain/value/cdomains/stringDomain.ml | 35 +++++++++++----------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/cdomain/value/cdomains/stringDomain.ml b/src/cdomain/value/cdomains/stringDomain.ml index 5ed704ce69..35054590f9 100644 --- a/src/cdomain/value/cdomains/stringDomain.ml +++ b/src/cdomain/value/cdomains/stringDomain.ml @@ -23,10 +23,9 @@ type t = string option [@@deriving eq, ord, hash] (** [None] means top. *) let hash x = - if get_string_domain () <> Unit then - hash x - else - 13859 + match get_string_domain () with + | Disjoint | Flat -> hash x + | Unit -> 13859 let show = function | Some x -> "\"" ^ x ^ "\"" @@ -40,10 +39,9 @@ include Printable.SimpleShow ( ) let of_string x = - if get_string_domain () = Unit then - None - else - Some x + match get_string_domain () with + | Unit -> None + | Disjoint | Flat -> Some x let to_string x = x (* only keep part before first null byte *) @@ -92,10 +90,10 @@ let join x y = | _, None -> None | Some a, Some b when a = b -> Some a | Some a, Some b (* when a <> b *) -> - if get_string_domain () = Disjoint then - raise Lattice.Uncomparable - else - None + match get_string_domain () with + | Disjoint -> raise Lattice.Uncomparable + | Flat -> None + | Unit -> assert false let meet x y = match x, y with @@ -103,13 +101,14 @@ let meet x y = | a, None -> a | Some a, Some b when a = b -> Some a | Some a, Some b (* when a <> b *) -> - if get_string_domain () = Disjoint then - raise Lattice.Uncomparable - else - raise Lattice.BotValue + match get_string_domain () with + | Disjoint -> raise Lattice.Uncomparable + | Flat -> raise Lattice.BotValue + | Unit -> assert false let repr x = - if get_string_domain () = Disjoint then + match get_string_domain () with + | Disjoint -> x (* everything else is kept separate, including strings if not limited *) - else + | Flat | Unit -> None (* all strings together if limited *) From bf0c28fd0ef83f3202b27e9b7a982dfa2b48b0cf Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 23 Oct 2024 16:48:07 +0300 Subject: [PATCH 447/566] Revert "Work around old LibraryFunctions spawning even with sem.unknown_function.spawn disabled" This reverts commit e5799bcb81dd50bea45bc36657fe0b28a703c95d. --- src/analyses/base.ml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index e429155e4d..cea2c8bcee 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2172,11 +2172,7 @@ struct in List.filter_map (create_thread ~multiple (Some (Mem id, NoOffset)) (Some ptc_arg)) start_funvars_with_unknown end - | _, _ when get_bool "sem.unknown_function.spawn" -> - (* TODO: Remove sem.unknown_function.spawn check because it is (and should be) really done in LibraryFunctions. - But here we consider all non-ThreadCreate functions also unknown, so old-style LibraryFunctions access - definitions using `Write would still spawn because they are not truly unknown functions (missing from LibraryFunctions). - Need this to not have memmove spawn in SV-COMP. *) + | _, _ -> let shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = false } args in let deep_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = true } args in let shallow_flist = collect_invalidate ~deep:false ~ctx ctx.local shallow_args in @@ -2185,7 +2181,6 @@ struct let addrs = List.concat_map AD.to_var_may flist in if addrs <> [] then M.debug ~category:Analyzer "Spawning non-unique functions from unknown function: %a" (d_list ", " CilType.Varinfo.pretty) addrs; List.filter_map (create_thread ~multiple:true None None) addrs - | _, _ -> [] let assert_fn ctx e refine = (* make the state meet the assertion in the rest of the code *) From 4d4de22cad91068002242a2942bad0c300da772b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 23 Oct 2024 17:05:36 +0300 Subject: [PATCH 448/566] Add option sem.atexit.ignore --- src/config/options.schema.json | 13 +++++++++++++ src/util/library/libraryFunctions.ml | 2 +- tests/regression/41-stdlib/08-atexit-no-spawn.c | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 447290b44d..5d87eb51f6 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -1544,6 +1544,19 @@ } }, "additionalProperties": false + }, + "atexit": { + "title": "sem.atexit", + "type": "object", + "properties": { + "ignore": { + "title": "sem.atexit.ignore", + "description": "Ignore atexit callbacks (unsound).", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index 25a90da2d3..31fcf0510e 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -145,7 +145,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *) ("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); ("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value }); - ("atexit", unknown [drop "function" [s]]); + ("atexit", unknown [drop "function" [if_ (fun () -> not (get_bool "sem.atexit.ignore")) s]]); ("atoi", unknown [drop "nptr" [r]]); ("atol", unknown [drop "nptr" [r]]); ("atoll", unknown [drop "nptr" [r]]); diff --git a/tests/regression/41-stdlib/08-atexit-no-spawn.c b/tests/regression/41-stdlib/08-atexit-no-spawn.c index 7f25f42183..3bbba82634 100644 --- a/tests/regression/41-stdlib/08-atexit-no-spawn.c +++ b/tests/regression/41-stdlib/08-atexit-no-spawn.c @@ -1,4 +1,4 @@ -// PARAM: --disable sem.unknown_function.spawn +// PARAM: --enable sem.atexit.ignore #include #include From 6b77fb3298d2d6f716510767d3a6411a91382348 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 23 Oct 2024 17:15:14 +0300 Subject: [PATCH 449/566] Generalize AutoTune.hasFunction predicate --- src/autoTune.ml | 10 ++++++---- src/util/autoSoundConfig.ml | 7 ++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 0def6021fa..843c977ae2 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -157,7 +157,7 @@ let hasFunction pred = Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo var) @@ fun () -> if LibraryFunctions.is_special var then let desc = LibraryFunctions.find var in - GobOption.exists (fun args -> pred (desc.special args)) (functionArgs var) + GobOption.exists (fun args -> pred desc args) (functionArgs var) else false in @@ -169,7 +169,7 @@ let hasFunction pred = match unrollType var.vtype with | TFun (_, args, _, _) -> let args = BatOption.map_default (List.map (fun (x,_,_) -> MyCFG.unknown_exp)) [] args in - pred (desc.special args) + pred desc args | _ -> false else false @@ -191,7 +191,8 @@ let enableAnalyses anas = let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"; "mhp"; "region"; "pthreadMutexType"] let reduceThreadAnalyses () = - let isThreadCreate = function + let isThreadCreate (desc: LibraryDesc.t) args = + match desc.special args with | LibraryDesc.ThreadCreate _ -> true | _ -> false in @@ -446,7 +447,8 @@ let wideningOption factors file = } let activateTmpSpecialAnalysis () = - let isMathFun = function + let isMathFun (desc: LibraryDesc.t) args = + match desc.special args with | LibraryDesc.Math _ -> true | _ -> false in diff --git a/src/util/autoSoundConfig.ml b/src/util/autoSoundConfig.ml index 7a30bdf5ce..0bb67e768e 100644 --- a/src/util/autoSoundConfig.ml +++ b/src/util/autoSoundConfig.ml @@ -12,8 +12,8 @@ let enableSpecAnalyses spec analyses = Logs.info "Specification: %s -> enabling soundness analyses \"%s\"" (Svcomp.Specification.to_string [spec]) (String.concat ", " analyses); enableAnalyses analyses -let enableOptions options = - let enableOpt option = +let enableOptions options = + let enableOpt option = Logs.info "Setting \"%s\" to true" option; set_bool option true in @@ -60,7 +60,8 @@ let enableAnalysesForSpecification () = let longjmpAnalyses = ["activeLongjmp"; "activeSetjmp"; "taintPartialContexts"; "modifiedSinceSetjmp"; "poisonVariables"; "expsplit"; "vla"] let activateLongjmpAnalysesWhenRequired () = - let isLongjmp = function + let isLongjmp (desc: LibraryDesc.t) args = + match desc.special args with | LibraryDesc.Longjmp _ -> true | _ -> false in From b00c608f70c115bc442d8c355653655541527d37 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 23 Oct 2024 17:19:48 +0300 Subject: [PATCH 450/566] Consider all spawning functions in autotuner (closes #1181) --- src/autoTune.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 843c977ae2..f59a10ee8a 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -194,7 +194,7 @@ let reduceThreadAnalyses () = let isThreadCreate (desc: LibraryDesc.t) args = match desc.special args with | LibraryDesc.ThreadCreate _ -> true - | _ -> false + | _ -> LibraryDesc.Accesses.find_kind desc.accs Spawn args <> [] in let hasThreadCreate = hasFunction isThreadCreate in if not @@ hasThreadCreate then ( From 3bb17bc06fd9901d6476a5db72f9af0be805e7e5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 24 Oct 2024 17:24:37 +0300 Subject: [PATCH 451/566] Add cram tests for some solvers --- tests/regression/00-sanity/01-assert.t | 130 +++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/tests/regression/00-sanity/01-assert.t b/tests/regression/00-sanity/01-assert.t index 9142f805f9..2f81310ada 100644 --- a/tests/regression/00-sanity/01-assert.t +++ b/tests/regression/00-sanity/01-assert.t @@ -9,3 +9,133 @@ live: 7 dead: 2 total lines: 9 + + +Test ancient solvers: + + $ goblint --enable warn.deterministic --set solver WL 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver effectWConEq 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + +Test topdown solvers: + + $ goblint --enable warn.deterministic --set solver topdown_deprecated 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver topdown 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver topdown_term 01-assert.c + [Error] Fixpoint not reached at L:entry state of main (299) on 01-assert.c:4:1-15:1 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} instead of bot + + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 4..7 (01-assert.c:4-7) + on lines 10..14 (01-assert.c:10-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 0 + dead: 9 + total lines: 9 + [Error][Unsound] Fixpoint not reached + [3] + + $ goblint --enable warn.deterministic --set solver topdown_space_cache_term 01-assert.c + [Error] Cannot find value 'solvers.wp.restore' in + {"files":["01-assert.c"],"outfile":"","justcil":false,"justcfg":false,"verify":true,"mainfun":["main"],"exitfun":[],"otherfun":[],"allglobs":false,"kernel":false,"dump_globs":false,"result":"none","solver":"topdown_space_cache_term","comparesolver":"","allfuns":false,"nonstatic":false,"colors":"auto","g2html":false,"save_run":"","load_run":"","compare_runs":[],"warn_at":"post","gobview":false,"jobs":1,"goblint-dir":".goblint","pre":{"enabled":true,"keep":false,"exist":false,"includes":[],"kernel_includes":[],"custom_includes":[],"kernel-root":"","cppflags":[],"compdb":{"original-path":"","split":false},"transform-paths":true},"cil":{"merge":{"inlines":true},"cstd":"c99","gnu89inline":false,"addNestedScopeAttr":false},"server":{"enabled":false,"mode":"stdio","unix-socket":"goblint.sock","reparse":false},"ana":{"activated":["expRelation","base","threadid","threadflag","threadreturn","escape","mutexEvents","mutex","access","race","mallocWrapper","mhp","assert","pthreadMutexType"],"path_sens":["mutex","malloc_null","uninit","expsplit","activeSetjmp","memLeak","apron","affeq","lin2vareq"],"ctx_insens":["stack_loc","stack_trace_set"],"ctx_sens":[],"setjmp":{"split":"precise"},"int":{"def_exc":true,"interval":false,"interval_set":false,"enums":false,"congruence":false,"refinement":"never","def_exc_widen_by_join":false,"interval_narrow_by_meet":false,"interval_threshold_widening":false,"interval_threshold_widening_constants":"all"},"float":{"interval":false,"evaluate_math_functions":false},"pml":{"debug":true},"opt":{"hashcons":true,"equal":true},"autotune":{"enabled":false,"activated":["congruence","singleThreaded","specification","mallocWrappers","noRecursiveIntervals","enums","loopUnrollHeuristic","arrayDomain","octagon","wideningThresholds","memsafetySpecification","termination","tmpSpecialAnalysis"]},"sv-comp":{"enabled":false,"functions":false},"specification":"","wp":false,"arrayoob":false,"base":{"context":{"non-ptr":true,"int":true,"interval":true,"interval_set":true},"strings":{"domain":"flat"},"partition-arrays":{"keep-expr":"first","partition-by-const-on-return":false,"smart-join":false},"arrays":{"domain":"trivial","unrolling-factor":0,"nullbytes":false},"structs":{"domain":"simple","key":{"forward":true,"avoid-ints":true,"prefer-ptrs":true}},"privatization":"protection-read","priv":{"not-started":true,"must-joined":true},"invariant":{"enabled":true,"blobs":false,"unassume":"once","int":{"simplify":"all"}},"eval":{"deep-query":true}},"malloc":{"wrappers":["kmalloc","__kmalloc","usb_alloc_urb","__builtin_alloca","kzalloc"],"unique_address_count":0},"apron":{"strengthening":false,"domain":"octagon","threshold_widening":false,"threshold_widening_constants":"all","invariant":{"diff-box":false}},"relation":{"context":true,"privatization":"mutex-meet","priv":{"not-started":true,"must-joined":true},"invariant":{"one-var":false,"local":true,"global":true}},"context":{"widen":false,"gas_value":-1,"gas_scope":"global","callString_length":2},"thread":{"domain":"history","include-node":true,"wrappers":[],"unique_thread_id_count":0,"context":{"create-edges":true}},"race":{"free":true,"call":true,"direct-arithmetic":false,"volatile":true},"dead-code":{"lines":true,"branches":true,"functions":true},"extract-pthread":{"assume_success":true,"ignore_assign":true},"widen":{"tokens":false},"unassume":{"precheck":false}},"incremental":{"load":false,"load-dir":"incremental_data","only-rename":false,"save":false,"save-dir":"incremental_data","stable":true,"wpoint":false,"reluctant":{"enabled":false},"compare":"ast","detect-renames":true,"force-reanalyze":{"funs":[]},"restart":{"sided":{"enabled":false,"vars":"all","fuel":-1,"fuel-only-global":false},"list":[],"write-only":true},"postsolver":{"enabled":true,"superstable-reached":false}},"lib":{"activated":["c","posix","pthread","gcc","glibc","linux-userspace","goblint","ncurses","legacy"]},"sem":{"unknown_function":{"spawn":true,"call":true,"invalidate":{"globals":true,"args":true},"read":{"args":true}},"builtin_unreachable":{"dead_code":false},"noreturn":{"dead_code":false},"int":{"signed_overflow":"assume_top"},"null-pointer":{"dereference":"assume_none"},"malloc":{"fail":false},"lock":{"fail":false},"assert":{"refine":true},"atexit":{"ignore":false}},"trans":{"activated":[],"expeval":{"query_file_name":""},"output":"transformed.c","assert":{"function":"__VERIFIER_assert","wrap-atomic":true}},"annotation":{"int":{"enabled":false,"privglobs":true},"float":{"enabled":false},"goblint_context":{"__additional__":[]},"goblint_precision":{"__additional__":[]},"goblint_array_domain":false,"goblint_relation_track":false},"exp":{"priv-prec-dump":"","priv-distr-init":false,"relation":{"prec-dump":""},"cfgdot":false,"mincfg":false,"earlyglobs":false,"region-offsets":false,"unique":[],"forward":false,"volatiles_are_top":true,"single-threaded":false,"globs_are_top":false,"exclude_from_earlyglobs":[],"exclude_from_invalidation":[],"g2html_path":"","extraspecials":[],"no-narrow":false,"basic-blocks":false,"fast_global_inits":true,"architecture":"64bit","gcc_path":"/usr/bin/gcc","cpp-path":"","unrolling-factor":0,"hide-std-globals":true,"arg":{"enabled":false,"dot":{"path":"","node-label":"node"}}},"dbg":{"level":"info","timing":{"enabled":false,"tef":""},"trace":{"context":false},"dump":"","cilout":"","justcil-printer":"default","timeout":"0","solver-stats-interval":10,"solver-signal":"sigusr1","backtrace-signal":"sigusr2","solver-progress":false,"print_wpoints":false,"slice":{"on":false,"n":10},"limit":{"widen":0},"warn_with_context":false,"regression":false,"test":{"domain":false},"cilcfgdot":false,"cfg":{"loop-clusters":false,"loop-unrolling":false},"compare_runs":{"globsys":false,"eqsys":true,"global":false,"node":false,"diff":false},"print_tids":false,"print_protection":false,"run_cil_check":false,"full-output":false},"warn":{"assert":true,"behavior":true,"call":true,"integer":true,"float":true,"cast":true,"race":true,"deadlock":true,"deadcode":true,"analyzer":true,"unsound":true,"imprecise":true,"witness":true,"program":true,"termination":true,"unknown":true,"error":true,"warning":true,"info":true,"debug":false,"success":true,"quote-code":false,"race-threshold":0,"deterministic":true,"memleak":{"memcleanup":false,"memtrack":false}},"solvers":{"td3":{"term":true,"side_widen":"sides","space":false,"space_cache":true,"space_restore":true,"narrow-reuse":true,"remove-wpoint":true,"skip-unchanged-rhs":false,"restart":{"wpoint":{"enabled":false,"once":false}},"verify":false},"slr4":{"restart_count":1}},"witness":{"graphml":{"enabled":false,"path":"witness.graphml","id":"node","minimize":false,"uncil":false,"stack":true,"unknown":true},"invariant":{"loop-head":true,"after-lock":true,"other":true,"split-conjunction":true,"accessed":false,"full":true,"exact":true,"inexact-type-bounds":false,"exclude-vars":["tmp\\(___[0-9]+\\)?","cond","RETURN"],"all-locals":true,"goblint":false,"typedefs":true},"yaml":{"enabled":false,"format-version":"0.1","entry-types":["location_invariant","loop_invariant","flow_insensitive_invariant","loop_invariant_certificate","precondition_loop_invariant_certificate","invariant_set"],"invariant-types":["location_invariant","loop_invariant"],"path":"witness.yml","validate":"","strict":false,"unassume":"","certificate":""}}} + Did You forget to add default values to options.schema.json? + + [Info] runtime: 00:00:00.061 + [Info] vars: 2, evals: 12 + [Info] max updates: 1 for var L:call of main (299) on 01-assert.c:4:1-15:1 + + + Memory statistics: total=23.99MB, max=7.06MB, minor=21.98MB, major=6.53MB, promoted=4.52MB + minor collections=10 major collections=1 compactions=0 + + + Fatal error: exception Failure("get_path_string") + [2] + + $ goblint --enable warn.deterministic --set solver td3 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 From e57072f7560c7a5f060087be37b041c3206aa224 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 24 Oct 2024 17:26:12 +0300 Subject: [PATCH 452/566] Fix option name in topdown_space_cache_term --- src/solver/topDown_space_cache_term.ml | 2 +- tests/regression/00-sanity/01-assert.t | 25 ++++++++++--------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/solver/topDown_space_cache_term.ml b/src/solver/topDown_space_cache_term.ml index 0022756a31..df44c376e1 100644 --- a/src/solver/topDown_space_cache_term.ml +++ b/src/solver/topDown_space_cache_term.ml @@ -170,7 +170,7 @@ module WP = ) in (* restore values for non-widening-points *) - if GobConfig.get_bool "solvers.wp.restore" then ( + if GobConfig.get_bool "solvers.td3.space_restore" then ( Logs.debug "Restoring missing values."; let restore () = let get x = diff --git a/tests/regression/00-sanity/01-assert.t b/tests/regression/00-sanity/01-assert.t index 2f81310ada..159fd5a932 100644 --- a/tests/regression/00-sanity/01-assert.t +++ b/tests/regression/00-sanity/01-assert.t @@ -112,21 +112,16 @@ Test topdown solvers: [3] $ goblint --enable warn.deterministic --set solver topdown_space_cache_term 01-assert.c - [Error] Cannot find value 'solvers.wp.restore' in - {"files":["01-assert.c"],"outfile":"","justcil":false,"justcfg":false,"verify":true,"mainfun":["main"],"exitfun":[],"otherfun":[],"allglobs":false,"kernel":false,"dump_globs":false,"result":"none","solver":"topdown_space_cache_term","comparesolver":"","allfuns":false,"nonstatic":false,"colors":"auto","g2html":false,"save_run":"","load_run":"","compare_runs":[],"warn_at":"post","gobview":false,"jobs":1,"goblint-dir":".goblint","pre":{"enabled":true,"keep":false,"exist":false,"includes":[],"kernel_includes":[],"custom_includes":[],"kernel-root":"","cppflags":[],"compdb":{"original-path":"","split":false},"transform-paths":true},"cil":{"merge":{"inlines":true},"cstd":"c99","gnu89inline":false,"addNestedScopeAttr":false},"server":{"enabled":false,"mode":"stdio","unix-socket":"goblint.sock","reparse":false},"ana":{"activated":["expRelation","base","threadid","threadflag","threadreturn","escape","mutexEvents","mutex","access","race","mallocWrapper","mhp","assert","pthreadMutexType"],"path_sens":["mutex","malloc_null","uninit","expsplit","activeSetjmp","memLeak","apron","affeq","lin2vareq"],"ctx_insens":["stack_loc","stack_trace_set"],"ctx_sens":[],"setjmp":{"split":"precise"},"int":{"def_exc":true,"interval":false,"interval_set":false,"enums":false,"congruence":false,"refinement":"never","def_exc_widen_by_join":false,"interval_narrow_by_meet":false,"interval_threshold_widening":false,"interval_threshold_widening_constants":"all"},"float":{"interval":false,"evaluate_math_functions":false},"pml":{"debug":true},"opt":{"hashcons":true,"equal":true},"autotune":{"enabled":false,"activated":["congruence","singleThreaded","specification","mallocWrappers","noRecursiveIntervals","enums","loopUnrollHeuristic","arrayDomain","octagon","wideningThresholds","memsafetySpecification","termination","tmpSpecialAnalysis"]},"sv-comp":{"enabled":false,"functions":false},"specification":"","wp":false,"arrayoob":false,"base":{"context":{"non-ptr":true,"int":true,"interval":true,"interval_set":true},"strings":{"domain":"flat"},"partition-arrays":{"keep-expr":"first","partition-by-const-on-return":false,"smart-join":false},"arrays":{"domain":"trivial","unrolling-factor":0,"nullbytes":false},"structs":{"domain":"simple","key":{"forward":true,"avoid-ints":true,"prefer-ptrs":true}},"privatization":"protection-read","priv":{"not-started":true,"must-joined":true},"invariant":{"enabled":true,"blobs":false,"unassume":"once","int":{"simplify":"all"}},"eval":{"deep-query":true}},"malloc":{"wrappers":["kmalloc","__kmalloc","usb_alloc_urb","__builtin_alloca","kzalloc"],"unique_address_count":0},"apron":{"strengthening":false,"domain":"octagon","threshold_widening":false,"threshold_widening_constants":"all","invariant":{"diff-box":false}},"relation":{"context":true,"privatization":"mutex-meet","priv":{"not-started":true,"must-joined":true},"invariant":{"one-var":false,"local":true,"global":true}},"context":{"widen":false,"gas_value":-1,"gas_scope":"global","callString_length":2},"thread":{"domain":"history","include-node":true,"wrappers":[],"unique_thread_id_count":0,"context":{"create-edges":true}},"race":{"free":true,"call":true,"direct-arithmetic":false,"volatile":true},"dead-code":{"lines":true,"branches":true,"functions":true},"extract-pthread":{"assume_success":true,"ignore_assign":true},"widen":{"tokens":false},"unassume":{"precheck":false}},"incremental":{"load":false,"load-dir":"incremental_data","only-rename":false,"save":false,"save-dir":"incremental_data","stable":true,"wpoint":false,"reluctant":{"enabled":false},"compare":"ast","detect-renames":true,"force-reanalyze":{"funs":[]},"restart":{"sided":{"enabled":false,"vars":"all","fuel":-1,"fuel-only-global":false},"list":[],"write-only":true},"postsolver":{"enabled":true,"superstable-reached":false}},"lib":{"activated":["c","posix","pthread","gcc","glibc","linux-userspace","goblint","ncurses","legacy"]},"sem":{"unknown_function":{"spawn":true,"call":true,"invalidate":{"globals":true,"args":true},"read":{"args":true}},"builtin_unreachable":{"dead_code":false},"noreturn":{"dead_code":false},"int":{"signed_overflow":"assume_top"},"null-pointer":{"dereference":"assume_none"},"malloc":{"fail":false},"lock":{"fail":false},"assert":{"refine":true},"atexit":{"ignore":false}},"trans":{"activated":[],"expeval":{"query_file_name":""},"output":"transformed.c","assert":{"function":"__VERIFIER_assert","wrap-atomic":true}},"annotation":{"int":{"enabled":false,"privglobs":true},"float":{"enabled":false},"goblint_context":{"__additional__":[]},"goblint_precision":{"__additional__":[]},"goblint_array_domain":false,"goblint_relation_track":false},"exp":{"priv-prec-dump":"","priv-distr-init":false,"relation":{"prec-dump":""},"cfgdot":false,"mincfg":false,"earlyglobs":false,"region-offsets":false,"unique":[],"forward":false,"volatiles_are_top":true,"single-threaded":false,"globs_are_top":false,"exclude_from_earlyglobs":[],"exclude_from_invalidation":[],"g2html_path":"","extraspecials":[],"no-narrow":false,"basic-blocks":false,"fast_global_inits":true,"architecture":"64bit","gcc_path":"/usr/bin/gcc","cpp-path":"","unrolling-factor":0,"hide-std-globals":true,"arg":{"enabled":false,"dot":{"path":"","node-label":"node"}}},"dbg":{"level":"info","timing":{"enabled":false,"tef":""},"trace":{"context":false},"dump":"","cilout":"","justcil-printer":"default","timeout":"0","solver-stats-interval":10,"solver-signal":"sigusr1","backtrace-signal":"sigusr2","solver-progress":false,"print_wpoints":false,"slice":{"on":false,"n":10},"limit":{"widen":0},"warn_with_context":false,"regression":false,"test":{"domain":false},"cilcfgdot":false,"cfg":{"loop-clusters":false,"loop-unrolling":false},"compare_runs":{"globsys":false,"eqsys":true,"global":false,"node":false,"diff":false},"print_tids":false,"print_protection":false,"run_cil_check":false,"full-output":false},"warn":{"assert":true,"behavior":true,"call":true,"integer":true,"float":true,"cast":true,"race":true,"deadlock":true,"deadcode":true,"analyzer":true,"unsound":true,"imprecise":true,"witness":true,"program":true,"termination":true,"unknown":true,"error":true,"warning":true,"info":true,"debug":false,"success":true,"quote-code":false,"race-threshold":0,"deterministic":true,"memleak":{"memcleanup":false,"memtrack":false}},"solvers":{"td3":{"term":true,"side_widen":"sides","space":false,"space_cache":true,"space_restore":true,"narrow-reuse":true,"remove-wpoint":true,"skip-unchanged-rhs":false,"restart":{"wpoint":{"enabled":false,"once":false}},"verify":false},"slr4":{"restart_count":1}},"witness":{"graphml":{"enabled":false,"path":"witness.graphml","id":"node","minimize":false,"uncil":false,"stack":true,"unknown":true},"invariant":{"loop-head":true,"after-lock":true,"other":true,"split-conjunction":true,"accessed":false,"full":true,"exact":true,"inexact-type-bounds":false,"exclude-vars":["tmp\\(___[0-9]+\\)?","cond","RETURN"],"all-locals":true,"goblint":false,"typedefs":true},"yaml":{"enabled":false,"format-version":"0.1","entry-types":["location_invariant","loop_invariant","flow_insensitive_invariant","loop_invariant_certificate","precondition_loop_invariant_certificate","invariant_set"],"invariant-types":["location_invariant","loop_invariant"],"path":"witness.yml","validate":"","strict":false,"unassume":"","certificate":""}}} - Did You forget to add default values to options.schema.json? - - [Info] runtime: 00:00:00.061 - [Info] vars: 2, evals: 12 - [Info] max updates: 1 for var L:call of main (299) on 01-assert.c:4:1-15:1 - - - Memory statistics: total=23.99MB, max=7.06MB, minor=21.98MB, major=6.53MB, promoted=4.52MB - minor collections=10 major collections=1 compactions=0 - - - Fatal error: exception Failure("get_path_string") - [2] + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 $ goblint --enable warn.deterministic --set solver td3 01-assert.c [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) From f0a15f65850b2ec295cb91c9005cca8265f479d9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 24 Oct 2024 17:28:39 +0300 Subject: [PATCH 453/566] Fix basic functionality of topdown_term --- src/solver/topDown_term.ml | 6 ++-- tests/regression/00-sanity/01-assert.t | 47 ++++---------------------- 2 files changed, 9 insertions(+), 44 deletions(-) diff --git a/src/solver/topDown_term.ml b/src/solver/topDown_term.ml index 9d89a42898..0099ec2115 100644 --- a/src/solver/topDown_term.ml +++ b/src/solver/topDown_term.ml @@ -24,8 +24,8 @@ module WP = let stable = HM.create 10 in let infl = HM.create 10 in (* y -> xs *) let called = HM.create 10 in - let rho = HM.create 10 in - let rho' = HM.create 10 in + let rho = HM.create 10 in (* rho for right-hand side values *) + let rho' = HM.create 10 in (* rho for start and side effect values *) let wpoint = HM.create 10 in let add_infl y x = @@ -101,7 +101,7 @@ module WP = let set_start (x,d) = if tracing then trace "sol2" "set_start %a ## %a" S.Var.pretty_trace x S.Dom.pretty d; init x; - HM.replace rho x d; + HM.replace rho' x d; solve x Widen in diff --git a/tests/regression/00-sanity/01-assert.t b/tests/regression/00-sanity/01-assert.t index 159fd5a932..60340fbd6e 100644 --- a/tests/regression/00-sanity/01-assert.t +++ b/tests/regression/00-sanity/01-assert.t @@ -65,51 +65,16 @@ Test topdown solvers: total lines: 9 $ goblint --enable warn.deterministic --set solver topdown_term 01-assert.c - [Error] Fixpoint not reached at L:entry state of main (299) on 01-assert.c:4:1-15:1 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), - mallocWrapper:(wrapper call:Unknown node, unique calls:{}), - base:({ - }, {}, {}, {}), - threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), - threadflag:Singlethreaded, - threadreturn:true, - escape:{}, - mutexEvents:(), - access:(), - mutex:(lockset:{}, multiplicity:{}), - race:(), - mhp:(), - assert:(), - pthreadMutexType:()], map:{})} - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), - mallocWrapper:(wrapper call:Unknown node, unique calls:{}), - base:({ - }, {}, {}, {}), - threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), - threadflag:Singlethreaded, - threadreturn:true, - escape:{}, - mutexEvents:(), - access:(), - mutex:(lockset:{}, multiplicity:{}), - race:(), - mhp:(), - assert:(), - pthreadMutexType:()], map:{})} instead of bot - + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) [Warning][Deadcode] Function 'main' does not return [Warning][Deadcode] Function 'main' has dead code: - on lines 4..7 (01-assert.c:4-7) - on lines 10..14 (01-assert.c:10-14) + on lines 13..14 (01-assert.c:13-14) [Warning][Deadcode] Logical lines of code (LLoC) summary: - live: 0 - dead: 9 + live: 7 + dead: 2 total lines: 9 - [Error][Unsound] Fixpoint not reached - [3] $ goblint --enable warn.deterministic --set solver topdown_space_cache_term 01-assert.c [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) From 1eb63b9ad85ade6bdbf268340a718bf2b00e27cd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 24 Oct 2024 17:31:27 +0300 Subject: [PATCH 454/566] Fix invalid widen call in topdown_term for side effects --- src/solver/sLR.ml | 2 +- src/solver/topDown_term.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver/sLR.ml b/src/solver/sLR.ml index 3b46f36f5e..0cf87caeee 100644 --- a/src/solver/sLR.ml +++ b/src/solver/sLR.ml @@ -66,7 +66,7 @@ module SLR3 = if tracing then trace "sol" "Contrib:%a" S.Dom.pretty tmp; let tmp = if wpx then - if HM.mem globals x then S.Dom.widen old tmp + if HM.mem globals x then S.Dom.widen old tmp (* TODO: no join in second argument, can call widen incorrectly? *) else box old tmp else tmp in diff --git a/src/solver/topDown_term.ml b/src/solver/topDown_term.ml index 0099ec2115..5560c50f4f 100644 --- a/src/solver/topDown_term.ml +++ b/src/solver/topDown_term.ml @@ -85,7 +85,7 @@ module WP = and side y d = let old = try HM.find rho' y with Not_found -> S.Dom.bot () in if not (S.Dom.leq d old) then ( - HM.replace rho' y (S.Dom.widen old d); + HM.replace rho' y (S.Dom.widen old (S.Dom.join old d)); HM.remove stable y; init y; solve y Widen; From c348dd6101acee5dcab78278f98b27e550072576 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 24 Oct 2024 17:38:36 +0300 Subject: [PATCH 455/566] Add cram tests for remaining solvers --- src/solver/sLR.ml | 2 +- tests/regression/00-sanity/01-assert.t | 297 +++++++++++++++++++++++++ 2 files changed, 298 insertions(+), 1 deletion(-) diff --git a/src/solver/sLR.ml b/src/solver/sLR.ml index 0cf87caeee..69d415307a 100644 --- a/src/solver/sLR.ml +++ b/src/solver/sLR.ml @@ -527,7 +527,7 @@ let _ = Selector.add_solver ("widen2", (module PostSolver.EqIncrSolverFromEqSolver (W2))); Selector.add_solver ("widen3", (module PostSolver.EqIncrSolverFromEqSolver (W3))); let module S2 = TwoPhased (struct let ver = 1 end) in - Selector.add_solver ("two", (module PostSolver.EqIncrSolverFromEqSolver (S2))); + Selector.add_solver ("two", (module PostSolver.EqIncrSolverFromEqSolver (S2))); (* TODO: broken even on 00-sanity/01-assert *) let module S1 = Make (struct let ver = 1 end) in Selector.add_solver ("new", (module PostSolver.EqIncrSolverFromEqSolver (S1))); Selector.add_solver ("slr+", (module PostSolver.EqIncrSolverFromEqSolver (S1))) diff --git a/tests/regression/00-sanity/01-assert.t b/tests/regression/00-sanity/01-assert.t index 60340fbd6e..cd8c4c06f8 100644 --- a/tests/regression/00-sanity/01-assert.t +++ b/tests/regression/00-sanity/01-assert.t @@ -99,3 +99,300 @@ Test topdown solvers: live: 7 dead: 2 total lines: 9 + + +Test SLR solvers: + + $ goblint --enable warn.deterministic --set solver widen1 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver widen2 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver widen3 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver two 01-assert.c + [Error] Fixpoint not reached at L:entry state of main (299) on 01-assert.c:4:1-15:1 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} instead of bot + + [Error] Fixpoint not reached at L:node 1 "success = 1;" on 01-assert.c:5:7-5:18 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot + + [Error] Fixpoint not reached at L:node 2 "silence = 1;" on 01-assert.c:6:7-6:18 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot + + [Error] Fixpoint not reached at L:node 3 "fail = 0;" on 01-assert.c:7:7-7:15 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot + + [Error] Fixpoint not reached at L:node 4 "__goblint_assert(success);" on 01-assert.c:10:3-10:28 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot + + [Error] Fixpoint not reached at L:node 5 "__goblint_assert(unknown == 4);" on 01-assert.c:11:3-11:33 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot + + [Error] Fixpoint not reached at L:node 6 "__goblint_assert(fail);" on 01-assert.c:12:3-12:25 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot + + [Error] Fixpoint not reached at L:node 7 "return (0);" on 01-assert.c:13:10-13:11 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot + + [Error] Fixpoint not reached at L:node 9 "__goblint_assert(silence);" on 01-assert.c:14:3-14:28 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot + + [Error] Fixpoint not reached at L:node -299 "return;" on 01-assert.c:15:1-15:1 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot + + [Error] Fixpoint not reached at L:call of main (299) on 01-assert.c:4:1-15:1 + Solver computed: + bot + Right-Hand-Side: + HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code + Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot + + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' is uncalled: 8 LLoC (01-assert.c:4:1-15:1) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 0 + dead: 8 (8 in uncalled functions) + total lines: 8 + [Error][Unsound] Fixpoint not reached + [3] + + $ goblint --enable warn.deterministic --set solver new 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver slr+ 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver slr1 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver slr2 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver slr3 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver slr4 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver slr1p 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + $ goblint --enable warn.deterministic --set solver slr2p 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver slr3p 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver slr4p 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver slr3t 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 + + $ goblint --enable warn.deterministic --set solver slr3tp 01-assert.c + [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) + [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) + [Success][Assert] Assertion "success" will succeed (01-assert.c:10:3-10:28) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on lines 13..14 (01-assert.c:13-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 2 + total lines: 9 From bfeaa22ae99fadfa916c35b96aa77e694ae1d8a9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 16:45:30 +0300 Subject: [PATCH 456/566] Rename IntDomain -> IntDomain0 for split Adds signatures to implementation to fix "contains type variables that cannot be generalized" errors. --- src/cdomain/value/cdomains/{intDomain.ml => intDomain0.ml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/cdomain/value/cdomains/{intDomain.ml => intDomain0.ml} (99%) diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain0.ml similarity index 99% rename from src/cdomain/value/cdomains/intDomain.ml rename to src/cdomain/value/cdomains/intDomain0.ml index e50b3f26cc..f4639d4522 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain0.ml @@ -1681,7 +1681,7 @@ struct let meet x y = if equal x y then x else bot () end -module Flat (Base: IkindUnawareS) = (* identical to Lift, but goes to `Top/`Bot if Base raises Unknown/Error *) +module Flat (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Lift, but goes to `Top/`Bot if Base raises Unknown/Error *) struct type int_t = Base.int_t include Lattice.FlatConf (struct @@ -1762,7 +1762,7 @@ struct | `Top | `Bot -> Invariant.none end -module Lift (Base: IkindUnawareS) = (* identical to Flat, but does not go to `Top/Bot` if Base raises Unknown/Error *) +module Lift (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Flat, but does not go to `Top/Bot` if Base raises Unknown/Error *) struct include Lattice.LiftPO (struct include Printable.DefaultConf From 6502779b680e7cf0d8331180968f4fcce5521592 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 16:46:20 +0300 Subject: [PATCH 457/566] Add intDomain.ml to redirect to IntDomain0 --- src/cdomain/value/cdomains/intDomain.ml | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/cdomain/value/cdomains/intDomain.ml diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml new file mode 100644 index 0000000000..5fa56f5b51 --- /dev/null +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -0,0 +1 @@ +include IntDomain0 From 727c6bf418e07451930f3cf6429b4f03955beed5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 16:47:48 +0300 Subject: [PATCH 458/566] Rename IntDomain0 -> IntDomTuple for split --- src/cdomain/value/cdomains/{intDomain0.ml => int/intDomTuple.ml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/cdomain/value/cdomains/{intDomain0.ml => int/intDomTuple.ml} (100%) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/int/intDomTuple.ml similarity index 100% rename from src/cdomain/value/cdomains/intDomain0.ml rename to src/cdomain/value/cdomains/int/intDomTuple.ml From 227eb70a17e9729296778a7c3fcc8b9cf3fd44a5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 16:49:03 +0300 Subject: [PATCH 459/566] Remove non-IntDomTuple parts --- src/cdomain/value/cdomains/int/intDomTuple.ml | 3271 +---------------- 1 file changed, 1 insertion(+), 3270 deletions(-) diff --git a/src/cdomain/value/cdomains/int/intDomTuple.ml b/src/cdomain/value/cdomains/int/intDomTuple.ml index f4639d4522..7420c989fc 100644 --- a/src/cdomain/value/cdomains/int/intDomTuple.ml +++ b/src/cdomain/value/cdomains/int/intDomTuple.ml @@ -1,3273 +1,4 @@ -open GobConfig -open GoblintCil -open Pretty -open PrecisionUtil - -module M = Messages - -let (%) = Batteries.(%) -let (|?) = Batteries.(|?) - -exception IncompatibleIKinds of string -exception Unknown -exception Error -exception ArithmeticOnIntegerBot of string - - - - -(** Define records that hold mutable variables representing different Configuration values. - * These values are used to keep track of whether or not the corresponding Config values are en-/disabled *) -type ana_int_config_values = { - mutable interval_threshold_widening : bool option; - mutable interval_narrow_by_meet : bool option; - mutable def_exc_widen_by_join : bool option; - mutable interval_threshold_widening_constants : string option; - mutable refinement : string option; -} - -let ana_int_config: ana_int_config_values = { - interval_threshold_widening = None; - interval_narrow_by_meet = None; - def_exc_widen_by_join = None; - interval_threshold_widening_constants = None; - refinement = None; -} - -let get_interval_threshold_widening () = - if ana_int_config.interval_threshold_widening = None then - ana_int_config.interval_threshold_widening <- Some (get_bool "ana.int.interval_threshold_widening"); - Option.get ana_int_config.interval_threshold_widening - -let get_interval_narrow_by_meet () = - if ana_int_config.interval_narrow_by_meet = None then - ana_int_config.interval_narrow_by_meet <- Some (get_bool "ana.int.interval_narrow_by_meet"); - Option.get ana_int_config.interval_narrow_by_meet - -let get_def_exc_widen_by_join () = - if ana_int_config.def_exc_widen_by_join = None then - ana_int_config.def_exc_widen_by_join <- Some (get_bool "ana.int.def_exc_widen_by_join"); - Option.get ana_int_config.def_exc_widen_by_join - -let get_interval_threshold_widening_constants () = - if ana_int_config.interval_threshold_widening_constants = None then - ana_int_config.interval_threshold_widening_constants <- Some (get_string "ana.int.interval_threshold_widening_constants"); - Option.get ana_int_config.interval_threshold_widening_constants - -let get_refinement () = - if ana_int_config.refinement = None then - ana_int_config.refinement <- Some (get_string "ana.int.refinement"); - Option.get ana_int_config.refinement - - - -(** Whether for a given ikind, we should compute with wrap-around arithmetic. - * Always for unsigned types, for signed types if 'sem.int.signed_overflow' is 'assume_wraparound' *) -let should_wrap ik = not (Cil.isSigned ik) || get_string "sem.int.signed_overflow" = "assume_wraparound" - -(** Whether for a given ikind, we should assume there are no overflows. - * Always false for unsigned types, true for signed types if 'sem.int.signed_overflow' is 'assume_none' *) -let should_ignore_overflow ik = Cil.isSigned ik && get_string "sem.int.signed_overflow" = "assume_none" - -let widening_thresholds = ResettableLazy.from_fun WideningThresholds.thresholds -let widening_thresholds_desc = ResettableLazy.from_fun (List.rev % WideningThresholds.thresholds) - -type overflow_info = { overflow: bool; underflow: bool;} - -let set_overflow_flag ~cast ~underflow ~overflow ik = - if !AnalysisState.executing_speculative_computations then - (* Do not produce warnings when the operations are not actually happening in code *) - () - else - let signed = Cil.isSigned ik in - if !AnalysisState.postsolving && signed && not cast then - AnalysisState.svcomp_may_overflow := true; - let sign = if signed then "Signed" else "Unsigned" in - match underflow, overflow with - | true, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190; CWE 191] "%s integer overflow and underflow" sign - | true, false -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 191] "%s integer underflow" sign - | false, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190] "%s integer overflow" sign - | false, false -> assert false - -let reset_lazy () = - ResettableLazy.reset widening_thresholds; - ResettableLazy.reset widening_thresholds_desc; - ana_int_config.interval_threshold_widening <- None; - ana_int_config.interval_narrow_by_meet <- None; - ana_int_config.def_exc_widen_by_join <- None; - ana_int_config.interval_threshold_widening_constants <- None; - ana_int_config.refinement <- None - -module type Arith = -sig - type t - val neg: t -> t - val add: t -> t -> t - val sub: t -> t -> t - val mul: t -> t -> t - val div: t -> t -> t - val rem: t -> t -> t - - val lt: t -> t -> t - val gt: t -> t -> t - val le: t -> t -> t - val ge: t -> t -> t - val eq: t -> t -> t - val ne: t -> t -> t - - val lognot: t -> t - val logand: t -> t -> t - val logor : t -> t -> t - val logxor: t -> t -> t - - val shift_left : t -> t -> t - val shift_right: t -> t -> t - - val c_lognot: t -> t - val c_logand: t -> t -> t - val c_logor : t -> t -> t - -end - -module type ArithIkind = -sig - type t - val neg: Cil.ikind -> t -> t - val add: Cil.ikind -> t -> t -> t - val sub: Cil.ikind -> t -> t -> t - val mul: Cil.ikind -> t -> t -> t - val div: Cil.ikind -> t -> t -> t - val rem: Cil.ikind -> t -> t -> t - - val lt: Cil.ikind -> t -> t -> t - val gt: Cil.ikind -> t -> t -> t - val le: Cil.ikind -> t -> t -> t - val ge: Cil.ikind -> t -> t -> t - val eq: Cil.ikind -> t -> t -> t - val ne: Cil.ikind -> t -> t -> t - - val lognot: Cil.ikind -> t -> t - val logand: Cil.ikind -> t -> t -> t - val logor : Cil.ikind -> t -> t -> t - val logxor: Cil.ikind -> t -> t -> t - - val shift_left : Cil.ikind -> t -> t -> t - val shift_right: Cil.ikind -> t -> t -> t - - val c_lognot: Cil.ikind -> t -> t - val c_logand: Cil.ikind -> t -> t -> t - val c_logor : Cil.ikind -> t -> t -> t - -end - -(* Shared functions between S and Z *) -module type B = -sig - include Lattice.S - type int_t - val bot_of: Cil.ikind -> t - val top_of: Cil.ikind -> t - val to_int: t -> int_t option - val equal_to: int_t -> t -> [`Eq | `Neq | `Top] - - val to_bool: t -> bool option - val to_excl_list: t -> (int_t list * (int64 * int64)) option - val of_excl_list: Cil.ikind -> int_t list -> t - val is_excl_list: t -> bool - - val to_incl_list: t -> int_t list option - - val maximal : t -> int_t option - val minimal : t -> int_t option - - val cast_to: ?suppress_ovwarn:bool -> ?torg:Cil.typ -> Cil.ikind -> t -> t -end - -(** Interface of IntDomain implementations that do not take ikinds for arithmetic operations yet. TODO: Should be ported to S in the future. *) -module type IkindUnawareS = -sig - include B - include Arith with type t := t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: int_t -> t - val of_bool: bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val arbitrary: unit -> t QCheck.arbitrary - val invariant: Cil.exp -> t -> Invariant.t -end - -(** Interface of IntDomain implementations taking an ikind for arithmetic operations *) -module type S = -sig - include B - include ArithIkind with type t:= t - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val neg : ?no_ov:bool -> Cil.ikind -> t -> t - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t - - val join: Cil.ikind -> t -> t -> t - val meet: Cil.ikind -> t -> t -> t - val narrow: Cil.ikind -> t -> t -> t - val widen: Cil.ikind -> t -> t -> t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val is_top_of: Cil.ikind -> t -> bool - val invariant_ikind : Cil.exp -> Cil.ikind -> t -> Invariant.t - - val refine_with_congruence: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_interval: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_excl_list: Cil.ikind -> t -> (int_t list * (int64 * int64)) option -> t - val refine_with_incl_list: Cil.ikind -> t -> int_t list option -> t - - val project: Cil.ikind -> int_precision -> t -> t - val arbitrary: Cil.ikind -> t QCheck.arbitrary -end - -module type SOverflow = -sig - - include S - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val neg : ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val of_int : Cil.ikind -> int_t -> t * overflow_info - - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t * overflow_info - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - - val shift_left : Cil.ikind -> t -> t -> t * overflow_info - - val shift_right : Cil.ikind -> t -> t -> t * overflow_info -end - -module type Y = -sig - (* include B *) - include B - include Arith with type t:= t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val is_top_of: Cil.ikind -> t -> bool - - val project: int_precision -> t -> t - val invariant: Cil.exp -> t -> Invariant.t -end - -module type Z = Y with type int_t = Z.t - - -module IntDomLifter (I : S) = -struct - open Cil - 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 (GobPretty.sprintf "ikinds %a and %a are incompatible. Values: %a and %a" CilType.Ikind.pretty x.ikind CilType.Ikind.pretty y.ikind I.pretty x.v I.pretty y.v)) - let lift op x = {x with v = op x.ikind x.v } - (* For logical operations the result is of type int *) - let lift_logical op x = {v = op x.ikind x.v; ikind = Cil.IInt} - let lift2 op x y = check_ikinds x y; {x with v = op x.ikind x.v y.v } - let lift2_cmp op x y = check_ikinds x y; {v = op x.ikind x.v y.v; ikind = Cil.IInt} - - let bot_of ikind = { v = I.bot_of ikind; ikind} - let bot () = failwith "bot () is not implemented for IntDomLifter." - let is_bot x = I.is_bot x.v - let top_of ikind = { v = I.top_of ikind; ikind} - let top () = failwith "top () is not implemented for IntDomLifter." - let is_top x = I.is_top x.v - - (* Leq does not check for ikind, because it is used in invariant with arguments of different type. - TODO: check ikinds here and fix invariant to work with right ikinds *) - let leq x y = I.leq x.v y.v - let join = lift2 I.join - let meet = lift2 I.meet - let widen = lift2 I.widen - let narrow = lift2 I.narrow - - let show x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - "⊤" - else - I.show x.v (* TODO add ikind to output *) - let pretty () x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - Pretty.text "⊤" - else - I.pretty () x.v (* TODO add ikind to output *) - let pretty_diff () (x, y) = I.pretty_diff () (x.v, y.v) (* TODO check ikinds, add them to output *) - let printXml o x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - BatPrintf.fprintf o "\n\n⊤\n\n\n" - else - I.printXml o x.v (* TODO add ikind to output *) - (* This is for debugging *) - let name () = "IntDomLifter(" ^ (I.name ()) ^ ")" - let to_yojson x = I.to_yojson x.v - let invariant e x = - let e' = Cilfacade.mkCast ~e ~newt:(TInt (x.ikind, [])) in - I.invariant_ikind e' x.ikind x.v - let tag x = I.tag x.v - let arbitrary ik = failwith @@ "Arbitrary not implement for " ^ (name ()) ^ "." - let to_int x = I.to_int x.v - let of_int ikind x = { v = I.of_int ikind x; ikind} - let equal_to i x = I.equal_to i x.v - let to_bool x = I.to_bool x.v - let of_bool ikind b = { v = I.of_bool ikind b; ikind} - let to_excl_list x = I.to_excl_list x.v - let of_excl_list ikind is = {v = I.of_excl_list ikind is; ikind} - let is_excl_list x = I.is_excl_list x.v - let to_incl_list x = I.to_incl_list x.v - let of_interval ?(suppress_ovwarn=false) ikind (lb,ub) = {v = I.of_interval ~suppress_ovwarn ikind (lb,ub); ikind} - let of_congruence ikind (c,m) = {v = I.of_congruence ikind (c,m); ikind} - let starting ?(suppress_ovwarn=false) ikind i = {v = I.starting ~suppress_ovwarn ikind i; ikind} - let ending ?(suppress_ovwarn=false) ikind i = {v = I.ending ~suppress_ovwarn ikind i; ikind} - let maximal x = I.maximal x.v - let minimal x = I.minimal x.v - - let neg = lift I.neg - let add = lift2 I.add - let sub = lift2 I.sub - let mul = lift2 I.mul - let div = lift2 I.div - let rem = lift2 I.rem - let lt = lift2_cmp I.lt - let gt = lift2_cmp I.gt - let le = lift2_cmp I.le - let ge = lift2_cmp I.ge - let eq = lift2_cmp I.eq - let ne = lift2_cmp I.ne - let lognot = lift I.lognot - let logand = lift2 I.logand - let logor = lift2 I.logor - let logxor = lift2 I.logxor - let shift_left x y = {x with v = I.shift_left x.ikind x.v y.v } (* TODO check ikinds*) - let shift_right x y = {x with v = I.shift_right x.ikind x.v y.v } (* TODO check ikinds*) - let c_lognot = lift_logical I.c_lognot - let c_logand = lift2 I.c_logand - let c_logor = lift2 I.c_logor - - let cast_to ?(suppress_ovwarn=false) ?torg ikind x = {v = I.cast_to ~suppress_ovwarn ~torg:(TInt(x.ikind,[])) ikind x.v; ikind} - - let is_top_of ik x = ik = x.ikind && I.is_top_of ik x.v - - let relift x = { v = I.relift x.v; ikind = x.ikind } - - let project p v = { v = I.project v.ikind p v.v; ikind = v.ikind } -end - -module type Ikind = -sig - val ikind: unit -> Cil.ikind -end - -module PtrDiffIkind : Ikind = -struct - let ikind = Cilfacade.ptrdiff_ikind -end - -module IntDomWithDefaultIkind (I: Y) (Ik: Ikind) : Y with type t = I.t and type int_t = I.int_t = -struct - include I - let top () = I.top_of (Ik.ikind ()) - let bot () = I.bot_of (Ik.ikind ()) -end - -module Size = struct (* size in bits as int, range as int64 *) - open Cil - let sign x = if Z.compare x Z.zero < 0 then `Signed else `Unsigned - - let top_typ = TInt (ILongLong, []) - let min_for x = intKindForValue x (sign x = `Unsigned) - let bit = function (* bits needed for representation *) - | IBool -> 1 - | ik -> bytesSizeOfInt ik * 8 - let is_int64_big_int x = Z.fits_int64 x - let card ik = (* cardinality *) - let b = bit ik in - Z.shift_left Z.one b - let bits ik = (* highest bits for neg/pos values *) - let s = bit ik in - if isSigned ik then s-1, s-1 else 0, s - let bits_i64 ik = BatTuple.Tuple2.mapn Int64.of_int (bits ik) - let range ik = - let a,b = bits ik in - let x = if isSigned ik then Z.neg (Z.shift_left Z.one a) (* -2^a *) else Z.zero in - let y = Z.pred (Z.shift_left Z.one b) in (* 2^b - 1 *) - x,y - - let is_cast_injective ~from_type ~to_type = - let (from_min, from_max) = range (Cilfacade.get_ikind from_type) in - let (to_min, to_max) = range (Cilfacade.get_ikind to_type) in - if M.tracing then M.trace "int" "is_cast_injective %a (%a, %a) -> %a (%a, %a)" CilType.Typ.pretty from_type GobZ.pretty from_min GobZ.pretty from_max CilType.Typ.pretty to_type GobZ.pretty to_min GobZ.pretty to_max; - Z.compare to_min from_min <= 0 && Z.compare from_max to_max <= 0 - - let cast t x = (* TODO: overflow is implementation-dependent! *) - if t = IBool then - (* C11 6.3.1.2 Boolean type *) - if Z.equal x Z.zero then Z.zero else Z.one - else - let a,b = range t in - let c = card t in - let y = Z.erem x c in - let y = if Z.gt y b then Z.sub y c - else if Z.lt y a then Z.add y c - else y - in - if M.tracing then M.tracel "cast" "Cast %a to range [%a, %a] (%a) = %a (%s in int64)" GobZ.pretty x GobZ.pretty a GobZ.pretty b GobZ.pretty c GobZ.pretty y (if is_int64_big_int y then "fits" else "does not fit"); - y - - let min_range_sign_agnostic x = - let size ik = - let a,b = bits_i64 ik in - Int64.neg a,b - in - if sign x = `Signed then - size (min_for x) - else - let a, b = size (min_for x) in - if b <= 64L then - let upper_bound_less = Int64.sub b 1L in - let max_one_less = Z.(pred @@ shift_left Z.one (Int64.to_int upper_bound_less)) in - if x <= max_one_less then - a, upper_bound_less - else - a,b - else - a, b - - (* From the number of bits used to represent a positive value, determines the maximal representable value *) - let max_from_bit_range pos_bits = Z.(pred @@ shift_left Z.one (to_int (Z.of_int64 pos_bits))) - - (* From the number of bits used to represent a non-positive value, determines the minimal representable value *) - let min_from_bit_range neg_bits = Z.(if neg_bits = 0L then Z.zero else neg @@ shift_left Z.one (to_int (neg (Z.of_int64 neg_bits)))) - -end - - -module StdTop (B: sig type t val top_of: Cil.ikind -> t end) = struct - open B - (* these should be overwritten for better precision if possible: *) - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ik x = top_of ik - let ending ?(suppress_ovwarn=false) ik x = top_of ik - let maximal x = None - let minimal x = None -end - -module Std (B: sig - type t - val name: unit -> string - val top_of: Cil.ikind -> t - val bot_of: Cil.ikind -> t - val show: t -> string - val equal: t -> t -> bool - end) = struct - include Printable.StdLeaf - let name = B.name (* overwrite the one from Printable.Std *) - open B - let is_top x = failwith "is_top not implemented for IntDomain.Std" - let is_bot x = B.equal x (bot_of Cil.IInt) (* Here we assume that the representation of bottom is independent of the ikind - This may be true for intdomain implementations, but not e.g. for IntDomLifter. *) - let is_top_of ik x = B.equal x (top_of ik) - - (* all output is based on B.show *) - include Printable.SimpleShow ( - struct - type nonrec t = t - let show = show - end - ) - let pretty_diff () (x,y) = dprintf "%s: %a instead of %a" (name ()) pretty x pretty y - - include StdTop (B) -end - -(* Textbook interval arithmetic, without any overflow handling etc. *) -module IntervalArith (Ints_t : IntOps.IntOps) = struct - let min4 a b c d = Ints_t.min (Ints_t.min a b) (Ints_t.min c d) - let max4 a b c d = Ints_t.max (Ints_t.max a b) (Ints_t.max c d) - - let mul (x1, x2) (y1, y2) = - let x1y1 = (Ints_t.mul x1 y1) in - let x1y2 = (Ints_t.mul x1 y2) in - let x2y1 = (Ints_t.mul x2 y1) in - let x2y2 = (Ints_t.mul x2 y2) in - (min4 x1y1 x1y2 x2y1 x2y2, max4 x1y1 x1y2 x2y1 x2y2) - - let shift_left (x1,x2) (y1,y2) = - let y1p = Ints_t.shift_left Ints_t.one y1 in - let y2p = Ints_t.shift_left Ints_t.one y2 in - mul (x1, x2) (y1p, y2p) - - let div (x1, x2) (y1, y2) = - let x1y1n = (Ints_t.div x1 y1) in - let x1y2n = (Ints_t.div x1 y2) in - let x2y1n = (Ints_t.div x2 y1) in - let x2y2n = (Ints_t.div x2 y2) in - let x1y1p = (Ints_t.div x1 y1) in - let x1y2p = (Ints_t.div x1 y2) in - let x2y1p = (Ints_t.div x2 y1) in - let x2y2p = (Ints_t.div x2 y2) in - (min4 x1y1n x1y2n x2y1n x2y2n, max4 x1y1p x1y2p x2y1p x2y2p) - - let add (x1, x2) (y1, y2) = (Ints_t.add x1 y1, Ints_t.add x2 y2) - let sub (x1, x2) (y1, y2) = (Ints_t.sub x1 y2, Ints_t.sub x2 y1) - - let neg (x1, x2) = (Ints_t.neg x2, Ints_t.neg x1) - - let one = (Ints_t.one, Ints_t.one) - let zero = (Ints_t.zero, Ints_t.zero) - let top_bool = (Ints_t.zero, Ints_t.one) - - let to_int (x1, x2) = - if Ints_t.equal x1 x2 then Some x1 else None - - let upper_threshold u max_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - let max_ik' = Ints_t.to_bigint max_ik in - let t = List.find_opt (fun x -> Z.compare u x <= 0 && Z.compare x max_ik' <= 0) ts in - BatOption.map_default Ints_t.of_bigint max_ik t - let lower_threshold l min_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - let min_ik' = Ints_t.to_bigint min_ik in - let t = List.find_opt (fun x -> Z.compare l x >= 0 && Z.compare x min_ik' >= 0) ts in - BatOption.map_default Ints_t.of_bigint min_ik t - let is_upper_threshold u = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - List.exists (Z.equal u) ts - let is_lower_threshold l = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - List.exists (Z.equal l) ts -end - -module IntInvariant = -struct - let of_int e ik x = - if get_bool "witness.invariant.exact" then - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) - else - Invariant.none - - let of_incl_list e ik ps = - match ps with - | [_; _] when ik = IBool && not (get_bool "witness.invariant.inexact-type-bounds") -> - assert (List.mem Z.zero ps); - assert (List.mem Z.one ps); - Invariant.none - | [_] when get_bool "witness.invariant.exact" -> - Invariant.none - | _ :: _ :: _ - | [_] | [] -> - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in - Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) (Invariant.bot ()) ps - - let of_interval_opt e ik = function - | (Some x1, Some x2) when Z.equal x1 x2 -> - of_int e ik x1 - | x1_opt, x2_opt -> - let (min_ik, max_ik) = Size.range ik in - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in - let i1 = - match x1_opt, inexact_type_bounds with - | Some x1, false when Z.equal min_ik x1 -> Invariant.none - | Some x1, _ -> Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1, e, intType)) - | None, _ -> Invariant.none - in - let i2 = - match x2_opt, inexact_type_bounds with - | Some x2, false when Z.equal x2 max_ik -> Invariant.none - | Some x2, _ -> Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) - | None, _ -> Invariant.none - in - Invariant.(i1 && i2) - - let of_interval e ik (x1, x2) = - of_interval_opt e ik (Some x1, Some x2) - - let of_excl_list e ik ns = - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in - Invariant.(a && i) - ) (Invariant.top ()) ns -end - -module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = -struct - let name () = "intervals" - type int_t = Ints_t.t - type t = (Ints_t.t * Ints_t.t) option [@@deriving eq, ord, hash] - module IArith = IntervalArith (Ints_t) - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - let top_of ik = Some (range ik) - let bot () = None - let bot_of ik = bot () (* TODO: improve *) - - let show = function None -> "bottom" | Some (x,y) -> "["^Ints_t.to_string x^","^Ints_t.to_string y^"]" - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let equal_to i = function - | None -> failwith "unsupported: equal_to with bottom" - | Some (a, b) -> - if a = b && b = i then `Eq else if Ints_t.compare a i <= 0 && Ints_t.compare i b <=0 then `Top else `Neq - - let norm ?(suppress_ovwarn=false) ?(cast=false) ik : (t -> t * overflow_info) = function None -> (None, {underflow=false; overflow=false}) | Some (x,y) -> - if Ints_t.compare x y > 0 then - (None,{underflow=false; overflow=false}) - else ( - let (min_ik, max_ik) = range ik in - let underflow = Ints_t.compare min_ik x > 0 in - let overflow = Ints_t.compare max_ik y < 0 in - let ov_info = { underflow = underflow && not suppress_ovwarn; overflow = overflow && not suppress_ovwarn } in - let v = - if underflow || overflow then - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (Ints_t.sub max_ik min_ik) in - let resdiff = Ints_t.abs (Ints_t.sub y x) in - if Ints_t.compare resdiff diff > 0 then - top_of ik - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if Ints_t.compare l u <= 0 then - Some (l, u) - else - (* Interval that wraps around (begins to the right of its end). We can not represent such intervals *) - top_of ik - else if not cast && should_ignore_overflow ik then - let tl, tu = BatOption.get @@ top_of ik in - Some (Ints_t.max tl x, Ints_t.min tu y) - else - top_of ik - else - Some (x,y) - in - (v, ov_info) - ) - - let leq (x:t) (y:t) = - match x, y with - | None, _ -> true - | Some _, None -> false - | Some (x1,x2), Some (y1,y2) -> Ints_t.compare x1 y1 >= 0 && Ints_t.compare x2 y2 <= 0 - - let join ik (x:t) y = - match x, y with - | None, z | z, None -> z - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.min x1 y1, Ints_t.max x2 y2) |> fst - - let meet ik (x:t) y = - match x, y with - | None, z | z, None -> None - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.max x1 y1, Ints_t.min x2 y2) |> fst - - (* TODO: change to_int signature so it returns a big_int *) - let to_int x = Option.bind x (IArith.to_int) - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm ~suppress_ovwarn ik @@ Some (x,y) - let of_int ik (x: int_t) = of_interval ik (x,x) - let zero = Some IArith.zero - let one = Some IArith.one - let top_bool = Some IArith.top_bool - - let of_bool _ik = function true -> one | false -> zero - let to_bool (a: t) = match a with - | None -> None - | Some (l, u) when Ints_t.compare l Ints_t.zero = 0 && Ints_t.compare u Ints_t.zero = 0 -> Some false - | x -> if leq zero x then None else Some true - - let starting ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (fst (range ik), n) - - (* TODO: change signature of maximal, minimal to return big_int*) - let maximal = function None -> None | Some (x,y) -> Some y - let minimal = function None -> None | Some (x,y) -> Some x - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov t = norm ~cast:true t (* norm does all overflow handling *) - - let widen ik x y = - match x, y with - | None, z | z, None -> z - | Some (l0,u0), Some (l1,u1) -> - let (min_ik, max_ik) = range ik in - let threshold = get_interval_threshold_widening () in - let l2 = - if Ints_t.compare l0 l1 = 0 then l0 - else if threshold then IArith.lower_threshold l1 min_ik - else min_ik - in - let u2 = - if Ints_t.compare u0 u1 = 0 then u0 - else if threshold then IArith.upper_threshold u1 max_ik - else max_ik - in - norm ik @@ Some (l2,u2) |> fst - let widen ik x y = - let r = widen ik x y in - if M.tracing && not (equal x y) then M.tracel "int" "interval widen %a %a -> %a" pretty x pretty y pretty r; - assert (leq x y); (* TODO: remove for performance reasons? *) - r - - let narrow ik x y = - match x, y with - | _,None | None, _ -> None - | Some (x1,x2), Some (y1,y2) -> - let threshold = get_interval_threshold_widening () in - let (min_ik, max_ik) = range ik in - let lr = if Ints_t.compare min_ik x1 = 0 || threshold && Ints_t.compare y1 x1 > 0 && IArith.is_lower_threshold x1 then y1 else x1 in - let ur = if Ints_t.compare max_ik x2 = 0 || threshold && Ints_t.compare y2 x2 < 0 && IArith.is_upper_threshold x2 then y2 else x2 in - norm ik @@ Some (lr,ur) |> fst - - - let narrow ik x y = - if get_interval_narrow_by_meet () then - meet ik x y - else - narrow ik x y - - let log f ~annihilator ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_bool i1, to_bool i2 with - | Some x, _ when x = annihilator -> of_bool ik annihilator - | _, Some y when y = annihilator -> of_bool ik annihilator - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - let c_logor = log (||) ~annihilator:true - let c_logand = log (&&) ~annihilator:false - - let log1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_bool i1 with - | Some x -> of_bool ik (f ik x) - | _ -> top_of ik - - let c_lognot = log1 (fun _ik -> not) - - let bit f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - let bitcomp f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{underflow=false; overflow=false})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let logxor = bit (fun _ik -> Ints_t.logxor) - - let logand ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (Ints_t.logand x y) |> fst with Division_by_zero -> top_of ik) - | _, Some y when Ints_t.equal y Ints_t.zero -> of_int ik Ints_t.zero |> fst - | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst - | _ -> top_of ik - - let logor = bit (fun _ik -> Ints_t.logor) - - let bit1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_int i1 with - | Some x -> of_int ik (f ik x) |> fst - | _ -> top_of ik - - let lognot = bit1 (fun _ik -> Ints_t.lognot) - let shift_right = bitcomp (fun _ik x y -> Ints_t.shift_right x (Ints_t.to_int y)) - - let neg ?no_ov ik = function None -> (None,{underflow=false; overflow=false}) | Some x -> norm ik @@ Some (IArith.neg x) - - let binary_op_with_norm ?no_ov op ik x y = match x, y with - | None, None -> (None, {overflow=false; underflow= false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some x, Some y -> norm ik @@ Some (op x y) - - let add ?no_ov = binary_op_with_norm IArith.add - let mul ?no_ov = binary_op_with_norm IArith.mul - let sub ?no_ov = binary_op_with_norm IArith.sub - - let shift_left ik a b = - match is_bot a, is_bot b with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show a) (show b))) - | _ -> - match a, minimal b, maximal b with - | Some a, Some bl, Some bu when (Ints_t.compare bl Ints_t.zero >= 0) -> - (try - let r = IArith.shift_left a (Ints_t.to_int bl, Ints_t.to_int bu) in - norm ik @@ Some r - with Z.Overflow -> (top_of ik,{underflow=false; overflow=true})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let rem ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (xl, xu), Some (yl, yu) -> - if is_top_of ik x && is_top_of ik y then - (* This is needed to preserve soundness also on things bigger than int32 e.g. *) - (* x: 3803957176L -> T in Interval32 *) - (* y: 4209861404L -> T in Interval32 *) - (* x % y: 3803957176L -> T in Interval32 *) - (* T in Interval32 is [-2147483648,2147483647] *) - (* the code below computes [-2147483647,2147483647] for this though which is unsound *) - top_of ik - else - (* If we have definite values, Ints_t.rem will give a definite result. - * Otherwise we meet with a [range] the result can be in. - * This range is [0, min xu b] if x is positive, and [max xl -b, min xu b] if x can be negative. - * The precise bound b is one smaller than the maximum bound. Negative y give the same result as positive. *) - let pos x = if Ints_t.compare x Ints_t.zero < 0 then Ints_t.neg x else x in - let b = Ints_t.sub (Ints_t.max (pos yl) (pos yu)) Ints_t.one in - let range = if Ints_t.compare xl Ints_t.zero>= 0 then Some (Ints_t.zero, Ints_t.min xu b) else Some (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit (fun _ik -> Ints_t.rem) ik x y) range - - let rec div ?no_ov ik x y = - match x, y with - | None, None -> (bot (),{underflow=false; overflow=false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | (Some (x1,x2) as x), (Some (y1,y2) as y) -> - begin - let is_zero v = Ints_t.compare v Ints_t.zero = 0 in - match y1, y2 with - | l, u when is_zero l && is_zero u -> (top_of ik,{underflow=false; overflow=false}) (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> div ik (Some (x1,x2)) (Some (Ints_t.one,y2)) - | _, u when is_zero u -> div ik (Some (x1,x2)) (Some (y1, Ints_t.(neg one))) - | _ when leq (of_int ik (Ints_t.zero) |> fst) (Some (y1,y2)) -> (top_of ik,{underflow=false; overflow=false}) - | _ -> binary_op_with_norm IArith.div ik x y - end - - let ne ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik true - else if Ints_t.compare x2 y1 <= 0 && Ints_t.compare y2 x1 <= 0 then - of_bool ik false - else top_bool - - let eq ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 && Ints_t.compare x2 y1 <= 0 then - of_bool ik true - else if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik false - else top_bool - - let ge ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 then of_bool ik true - else if Ints_t.compare x2 y1 < 0 then of_bool ik false - else top_bool - - let le ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 <= 0 then of_bool ik true - else if Ints_t.compare y2 x1 < 0 then of_bool ik false - else top_bool - - let gt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 then of_bool ik true - else if Ints_t.compare x2 y1 <= 0 then of_bool ik false - else top_bool - - let lt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 < 0 then of_bool ik true - else if Ints_t.compare y2 x1 <= 0 then of_bool ik false - else top_bool - - let invariant_ikind e ik = function - | Some (x1, x2) -> - let (x1', x2') = BatTuple.Tuple2.mapn Ints_t.to_bigint (x1, x2) in - IntInvariant.of_interval e ik (x1', x2') - | None -> Invariant.none - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let shrink = function - | Some (l, u) -> (return None) <+> (GobQCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) - | None -> empty - in - QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) (fun x -> of_interval ik x |> fst ) pair_arb) - - let modulo n k = - let result = Ints_t.rem n k in - if Ints_t.compare result Ints_t.zero >= 0 then result - else Ints_t.add result k - - let refine_with_congruence ik (intv : t) (cong : (int_t * int_t ) option) : t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if Ints_t.equal m Ints_t.zero && (Ints_t.compare c x < 0 || Ints_t.compare c y > 0) then None - else if Ints_t.equal m Ints_t.zero then - Some (c, c) - else - let (min_ik, max_ik) = range ik in - let rcx = - if Ints_t.equal x min_ik then x else - Ints_t.add x (modulo (Ints_t.sub c x) (Ints_t.abs m)) in - let lcy = - if Ints_t.equal y max_ik then y else - Ints_t.sub y (modulo (Ints_t.sub y c) (Ints_t.abs m)) in - if Ints_t.compare rcx lcy > 0 then None - else if Ints_t.equal rcx lcy then norm ik @@ Some (rcx, rcx) |> fst - else norm ik @@ Some (rcx, lcy) |> fst - | _ -> None - - let refine_with_congruence ik x y = - let refn = refine_with_congruence ik x y in - if M.tracing then M.trace "refine" "int_refine_with_congruence %a %a -> %a" pretty x pretty y pretty refn; - refn - - let refine_with_interval ik a b = meet ik a b - - let refine_with_excl_list ik (intv : t) (excl : (int_t list * (int64 * int64)) option) : t = - match intv, excl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls, (rl, rh)) -> - let rec shrink op b = - let new_b = (op b (Ints_t.of_int(Bool.to_int(BatList.mem_cmp Ints_t.compare b ls)))) in - if not (Ints_t.equal b new_b) then shrink op new_b else new_b - in - let (min_ik, max_ik) = range ik in - let l' = if Ints_t.equal l min_ik then l else shrink Ints_t.add l in - let u' = if Ints_t.equal u max_ik then u else shrink Ints_t.sub u in - let intv' = norm ik @@ Some (l', u') |> fst in - let range = norm ~suppress_ovwarn:true ik (Some (Ints_t.of_bigint (Size.min_from_bit_range rl), Ints_t.of_bigint (Size.max_from_bit_range rh))) |> fst in - meet ik intv' range - - let refine_with_incl_list ik (intv: t) (incl : (int_t list) option) : t = - match intv, incl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls) -> - let rec min m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> min (Some x) xs | Some m -> if Ints_t.compare m x < 0 then min (Some m) xs else min (Some x) xs in - let rec max m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> max (Some x) xs | Some m -> if Ints_t.compare m x > 0 then max (Some m) xs else max (Some x) xs in - match min None ls, max None ls with - | Some m1, Some m2 -> refine_with_interval ik (Some(l, u)) (Some (m1, m2)) - | _, _-> intv - - let project ik p t = t -end - -(** IntervalSetFunctor that is not just disjunctive completion of intervals, but attempts to be precise for wraparound arithmetic for unsigned types *) -module IntervalSetFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) list = -struct - - module Interval = IntervalFunctor (Ints_t) - module IArith = IntervalArith (Ints_t) - - - let name () = "interval_sets" - - type int_t = Ints_t.t - - let (>.) a b = Ints_t.compare a b > 0 - let (=.) a b = Ints_t.compare a b = 0 - let (<.) a b = Ints_t.compare a b < 0 - let (>=.) a b = Ints_t.compare a b >= 0 - let (<=.) a b = Ints_t.compare a b <= 0 - let (+.) a b = Ints_t.add a b - let (-.) a b = Ints_t.sub a b - - (* - Each domain's element is guaranteed to be in canonical form. That is, each interval contained - inside the set does not overlap with each other and they are not adjacent. - *) - type t = (Ints_t.t * Ints_t.t) list [@@deriving eq, hash, ord] - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - - let top_of ik = [range ik] - - let bot () = [] - - let bot_of ik = bot () - - let show (x: t) = - let show_interval i = Printf.sprintf "[%s, %s]" (Ints_t.to_string (fst i)) (Ints_t.to_string (snd i)) in - List.fold_left (fun acc i -> (show_interval i) :: acc) [] x |> List.rev |> String.concat ", " |> Printf.sprintf "[%s]" - - (* New type definition for the sweeping line algorithm used for implementing join/meet functions. *) - type event = Enter of Ints_t.t | Exit of Ints_t.t - - let unbox_event = function Enter x -> x | Exit x -> x - - let cmp_events x y = - (* Deliberately comparing ints first => Cannot be derived *) - let res = Ints_t.compare (unbox_event x) (unbox_event y) in - if res <> 0 then res - else - begin - match (x, y) with - | (Enter _, Exit _) -> -1 - | (Exit _, Enter _) -> 1 - | (_, _) -> 0 - end - - let interval_set_to_events (xs: t) = - List.concat_map (fun (a, b) -> [Enter a; Exit b]) xs - - let two_interval_sets_to_events (xs: t) (ys: t) = - let xs = interval_set_to_events xs in - let ys = interval_set_to_events ys in - List.merge cmp_events xs ys - - (* Using the sweeping line algorithm, combined_event_list returns a new event list representing the intervals in which at least n intervals in xs overlap - This function is used for both join and meet operations with different parameter n: 1 for join, 2 for meet *) - let combined_event_list lattice_op (xs:event list) = - let l = match lattice_op with `Join -> 1 | `Meet -> 2 in - let aux (interval_count, acc) = function - | Enter x -> (interval_count + 1, if (interval_count + 1) >= l && interval_count < l then (Enter x)::acc else acc) - | Exit x -> (interval_count - 1, if interval_count >= l && (interval_count - 1) < l then (Exit x)::acc else acc) - in - List.fold_left aux (0, []) xs |> snd |> List.rev - - let rec events_to_intervals = function - | [] -> [] - | (Enter x)::(Exit y)::xs -> (x, y)::(events_to_intervals xs) - | _ -> failwith "Invalid events list" - - let remove_empty_gaps (xs: t) = - let aux acc (l, r) = match acc with - | ((a, b)::acc') when (b +. Ints_t.one) >=. l -> (a, r)::acc' - | _ -> (l, r)::acc - in - List.fold_left aux [] xs |> List.rev - - let canonize (xs: t) = - interval_set_to_events xs |> - List.sort cmp_events |> - combined_event_list `Join |> - events_to_intervals |> - remove_empty_gaps - - let unop (x: t) op = match x with - | [] -> [] - | _ -> canonize @@ List.concat_map op x - - let binop (x: t) (y: t) op : t = match x, y with - | [], _ -> [] - | _, [] -> [] - | _, _ -> canonize @@ List.concat_map op (BatList.cartesian_product x y) - - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let minimal = function - | [] -> None - | (x, _)::_ -> Some x - - let maximal = function - | [] -> None - | xs -> Some (BatList.last xs |> snd) - - let equal_to_interval i (a, b) = - if a =. b && b =. i then - `Eq - else if a <=. i && i <=. b then - `Top - else - `Neq - - let equal_to i xs = match List.map (equal_to_interval i) xs with - | [] -> failwith "unsupported: equal_to with bottom" - | [`Eq] -> `Eq - | ys when List.for_all ((=) `Neq) ys -> `Neq - | _ -> `Top - - let norm_interval ?(suppress_ovwarn=false) ?(cast=false) ik (x,y) : t*overflow_info = - if x >. y then - ([],{underflow=false; overflow=false}) - else - let (min_ik, max_ik) = range ik in - let underflow = min_ik >. x in - let overflow = max_ik <. y in - let v = if underflow || overflow then - begin - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (max_ik -. min_ik) in - let resdiff = Ints_t.abs (y -. x) in - if resdiff >. diff then - [range ik] - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if l <=. u then - [(l, u)] - else - (* Interval that wraps around (begins to the right of its end). We CAN represent such intervals *) - [(min_ik, u); (l, max_ik)] - else if not cast && should_ignore_overflow ik then - [Ints_t.max min_ik x, Ints_t.min max_ik y] - else - [range ik] - end - else - [(x,y)] - in - if suppress_ovwarn then (v, {underflow=false; overflow=false}) else (v, {underflow; overflow}) - - let norm_intvs ?(suppress_ovwarn=false) ?(cast=false) (ik:ikind) (xs: t) : t*overflow_info = - let res = List.map (norm_interval ~suppress_ovwarn ~cast ik) xs in - let intvs = List.concat_map fst res in - let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in - let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in - (canonize intvs,{underflow; overflow}) - - let binary_op_with_norm op (ik:ikind) (x: t) (y: t) : t*overflow_info = match x, y with - | [], _ -> ([],{overflow=false; underflow=false}) - | _, [] -> ([],{overflow=false; underflow=false}) - | _, _ -> norm_intvs ik @@ List.concat_map (fun (x,y) -> [op x y]) (BatList.cartesian_product x y) - - let binary_op_with_ovc (x: t) (y: t) op : t*overflow_info = match x, y with - | [], _ -> ([],{overflow=false; underflow=false}) - | _, [] -> ([],{overflow=false; underflow=false}) - | _, _ -> - let res = List.map op (BatList.cartesian_product x y) in - let intvs = List.concat_map fst res in - let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in - let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in - (canonize intvs,{underflow; overflow}) - - let unary_op_with_norm op (ik:ikind) (x: t) = match x with - | [] -> ([],{overflow=false; underflow=false}) - | _ -> norm_intvs ik @@ List.concat_map (fun x -> [op x]) x - - let rec leq (xs: t) (ys: t) = - let leq_interval (al, au) (bl, bu) = al >=. bl && au <=. bu in - match xs, ys with - | [], _ -> true - | _, [] -> false - | (xl,xr)::xs', (yl,yr)::ys' -> - if leq_interval (xl,xr) (yl,yr) then - leq xs' ys - else if xr <. yl then - false - else - leq xs ys' - - let join ik (x: t) (y: t): t = - two_interval_sets_to_events x y |> - combined_event_list `Join |> - events_to_intervals |> - remove_empty_gaps - - let meet ik (x: t) (y: t): t = - two_interval_sets_to_events x y |> - combined_event_list `Meet |> - events_to_intervals - - let to_int = function - | [x] -> IArith.to_int x - | _ -> None - - let zero = [IArith.zero] - let one = [IArith.one] - let top_bool = [IArith.top_bool] - - let not_bool (x:t) = - let is_false x = equal x zero in - let is_true x = equal x one in - if is_true x then zero else if is_false x then one else top_bool - - let to_bool = function - | [(l,u)] when l =. Ints_t.zero && u =. Ints_t.zero -> Some false - | x -> if leq zero x then None else Some true - - let of_bool _ = function true -> one | false -> zero - - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm_interval ~suppress_ovwarn ~cast:false ik (x,y) - - let of_int ik (x: int_t) = of_interval ik (x, x) - - let lt ik x y = - match x, y with - | [], [] -> bot_of ik - | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> - let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in - let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in - if max_x <. min_y then - of_bool ik true - else if min_x >=. max_y then - of_bool ik false - else - top_bool - - let le ik x y = - match x, y with - | [], [] -> bot_of ik - | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> - let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in - let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in - if max_x <=. min_y then - of_bool ik true - else if min_x >. max_y then - of_bool ik false - else - top_bool - - let gt ik x y = not_bool @@ le ik x y - - let ge ik x y = not_bool @@ lt ik x y - - let eq ik x y = match x, y with - | (a, b)::[], (c, d)::[] when a =. b && c =. d && a =. c -> - one - | _ -> - if is_bot (meet ik x y) then - zero - else - top_bool - - let ne ik x y = not_bool @@ eq ik x y - let interval_to_int i = Interval.to_int (Some i) - let interval_to_bool i = Interval.to_bool (Some i) - - let log f ik (i1, i2) = - match (interval_to_bool i1, interval_to_bool i2) with - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - - let bit f ik (i1, i2) = - match (interval_to_int i1), (interval_to_int i2) with - | Some x, Some y -> (try of_int ik (f x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - - let bitcomp f ik (i1, i2) = - match (interval_to_int i1, interval_to_int i2) with - | Some x, Some y -> (try of_int ik (f x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{overflow=false; underflow=false})) - | _, _ -> (top_of ik,{overflow=false; underflow=false}) - - let logand ik x y = - let interval_logand = bit Ints_t.logand ik in - binop x y interval_logand - - let logor ik x y = - let interval_logor = bit Ints_t.logor ik in - binop x y interval_logor - - let logxor ik x y = - let interval_logxor = bit Ints_t.logxor ik in - binop x y interval_logxor - - let lognot ik x = - let interval_lognot i = - match interval_to_int i with - | Some x -> of_int ik (Ints_t.lognot x) |> fst - | _ -> top_of ik - in - unop x interval_lognot - - let shift_left ik x y = - let interval_shiftleft = bitcomp (fun x y -> Ints_t.shift_left x (Ints_t.to_int y)) ik in - binary_op_with_ovc x y interval_shiftleft - - let shift_right ik x y = - let interval_shiftright = bitcomp (fun x y -> Ints_t.shift_right x (Ints_t.to_int y)) ik in - binary_op_with_ovc x y interval_shiftright - - let c_lognot ik x = - let log1 f ik i1 = - match interval_to_bool i1 with - | Some x -> of_bool ik (f x) - | _ -> top_of ik - in - let interval_lognot = log1 not ik in - unop x interval_lognot - - let c_logand ik x y = - let interval_logand = log (&&) ik in - binop x y interval_logand - - let c_logor ik x y = - let interval_logor = log (||) ik in - binop x y interval_logor - - let add ?no_ov = binary_op_with_norm IArith.add - let sub ?no_ov = binary_op_with_norm IArith.sub - let mul ?no_ov = binary_op_with_norm IArith.mul - let neg ?no_ov = unary_op_with_norm IArith.neg - - let div ?no_ov ik x y = - let rec interval_div x (y1, y2) = begin - let top_of ik = top_of ik |> List.hd in - let is_zero v = v =. Ints_t.zero in - match y1, y2 with - | l, u when is_zero l && is_zero u -> top_of ik (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> interval_div x (Ints_t.one,y2) - | _, u when is_zero u -> interval_div x (y1, Ints_t.(neg one)) - | _ when leq (of_int ik (Ints_t.zero) |> fst) ([(y1,y2)]) -> top_of ik - | _ -> IArith.div x (y1, y2) - end - in binary_op_with_norm interval_div ik x y - - let rem ik x y = - let interval_rem (x, y) = - if Interval.is_top_of ik (Some x) && Interval.is_top_of ik (Some y) then - top_of ik - else - let (xl, xu) = x in let (yl, yu) = y in - let pos x = if x <. Ints_t.zero then Ints_t.neg x else x in - let b = (Ints_t.max (pos yl) (pos yu)) -. Ints_t.one in - let range = if xl >=. Ints_t.zero then (Ints_t.zero, Ints_t.min xu b) else (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit Ints_t.rem ik (x, y)) [range] - in - binop x y interval_rem - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik x = norm_intvs ~cast:true ik x - - (* - narrows down the extremeties of xs if they are equal to boundary values of the ikind with (possibly) narrower values from ys - *) - let narrow ik xs ys = match xs ,ys with - | [], _ -> [] | _ ,[] -> xs - | _, _ -> - let min_xs = minimal xs |> Option.get in - let max_xs = maximal xs |> Option.get in - let min_ys = minimal ys |> Option.get in - let max_ys = maximal ys |> Option.get in - let min_range,max_range = range ik in - let threshold = get_interval_threshold_widening () in - let min = if min_xs =. min_range || threshold && min_ys >. min_xs && IArith.is_lower_threshold min_xs then min_ys else min_xs in - let max = if max_xs =. max_range || threshold && max_ys <. max_xs && IArith.is_upper_threshold max_xs then max_ys else max_xs in - xs - |> (function (_, y)::z -> (min, y)::z | _ -> []) - |> List.rev - |> (function (x, _)::z -> (x, max)::z | _ -> []) - |> List.rev - - (* - 1. partitions the intervals of xs by assigning each of them to the an interval in ys that includes it. - and joins all intervals in xs assigned to the same interval in ys as one interval. - 2. checks for every pair of adjacent pairs whether the pairs did approach (if you compare the intervals from xs and ys) and merges them if it is the case. - 3. checks whether partitions at the extremeties are approaching infinity (and expands them to infinity. in that case) - - The expansion (between a pair of adjacent partitions or at extremeties ) stops at a threshold. - *) - let widen ik xs ys = - let (min_ik,max_ik) = range ik in - let threshold = get_bool "ana.int.interval_threshold_widening" in - let upper_threshold (_,u) = IArith.upper_threshold u max_ik in - let lower_threshold (l,_) = IArith.lower_threshold l min_ik in - (*obtain partitioning of xs intervals according to the ys interval that includes them*) - let rec interval_sets_to_partitions (ik: ikind) (acc : (int_t * int_t) option) (xs: t) (ys: t)= - match xs,ys with - | _, [] -> [] - | [], (y::ys) -> (acc,y):: interval_sets_to_partitions ik None [] ys - | (x::xs), (y::ys) when Interval.leq (Some x) (Some y) -> interval_sets_to_partitions ik (Interval.join ik acc (Some x)) xs (y::ys) - | (x::xs), (y::ys) -> (acc,y) :: interval_sets_to_partitions ik None (x::xs) ys - in - let interval_sets_to_partitions ik xs ys = interval_sets_to_partitions ik None xs ys in - (*merge a pair of adjacent partitions*) - let merge_pair ik (a,b) (c,d) = - let new_a = function - | None -> Some (upper_threshold b, upper_threshold b) - | Some (ax,ay) -> Some (ax, upper_threshold b) - in - let new_c = function - | None -> Some (lower_threshold d, lower_threshold d) - | Some (cx,cy) -> Some (lower_threshold d, cy) - in - if threshold && (lower_threshold d +. Ints_t.one) >. (upper_threshold b) then - [(new_a a,(fst b, upper_threshold b)); (new_c c, (lower_threshold d, snd d))] - else - [(Interval.join ik a c, (Interval.join ik (Some b) (Some d) |> Option.get))] - in - let partitions_are_approaching part_left part_right = match part_left, part_right with - | (Some (_, left_x), (_, left_y)), (Some (right_x, _), (right_y, _)) -> (right_x -. left_x) >. (right_y -. left_y) - | _,_ -> false - in - (*merge all approaching pairs of adjacent partitions*) - let rec merge_list ik = function - | [] -> [] - | x::y::xs when partitions_are_approaching x y -> merge_list ik ((merge_pair ik x y) @ xs) - | x::xs -> x :: merge_list ik xs - in - (*expands left extremity*) - let widen_left = function - | [] -> [] - | (None,(lb,rb))::ts -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (None, (lt,rb))::ts - | (Some (la,ra), (lb,rb))::ts when lb <. la -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (Some (la,ra),(lt,rb))::ts - | x -> x - in - (*expands right extremity*) - let widen_right x = - let map_rightmost = function - | [] -> [] - | (None,(lb,rb))::ts -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (None, (lb,ut))::ts - | (Some (la,ra), (lb,rb))::ts when ra <. rb -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (Some (la,ra),(lb,ut))::ts - | x -> x - in - List.rev x |> map_rightmost |> List.rev - in - interval_sets_to_partitions ik xs ys |> merge_list ik |> widen_left |> widen_right |> List.map snd - - let starting ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (fst (range ik), n) - - let invariant_ikind e ik xs = - List.map (fun x -> Interval.invariant_ikind e ik (Some x)) xs |> - let open Invariant in List.fold_left (||) (bot ()) - - let modulo n k = - let result = Ints_t.rem n k in - if result >=. Ints_t.zero then result - else result +. k - - let refine_with_congruence ik (intvs: t) (cong: (int_t * int_t ) option): t = - let refine_with_congruence_interval ik (cong : (int_t * int_t ) option) (intv : (int_t * int_t ) option): t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if m =. Ints_t.zero && (c <. x || c >. y) then [] - else if m =. Ints_t.zero then - [(c, c)] - else - let (min_ik, max_ik) = range ik in - let rcx = - if x =. min_ik then x else - x +. (modulo (c -. x) (Ints_t.abs m)) in - let lcy = - if y =. max_ik then y else - y -. (modulo (y -. c) (Ints_t.abs m)) in - if rcx >. lcy then [] - else if rcx =. lcy then norm_interval ik (rcx, rcx) |> fst - else norm_interval ik (rcx, lcy) |> fst - | _ -> [] - in - List.concat_map (fun x -> refine_with_congruence_interval ik cong (Some x)) intvs - - let refine_with_interval ik xs = function None -> [] | Some (a,b) -> meet ik xs [(a,b)] - - let refine_with_incl_list ik intvs = function - | None -> intvs - | Some xs -> meet ik intvs (List.map (fun x -> (x,x)) xs) - - let excl_range_to_intervalset (ik: ikind) ((min, max): int_t * int_t) (excl: int_t): t = - let intv1 = (min, excl -. Ints_t.one) in - let intv2 = (excl +. Ints_t.one, max) in - norm_intvs ik ~suppress_ovwarn:true [intv1 ; intv2] |> fst - - let of_excl_list ik (excls: int_t list) = - let excl_list = List.map (excl_range_to_intervalset ik (range ik)) excls in - let res = List.fold_left (meet ik) (top_of ik) excl_list in - res - - let refine_with_excl_list ik (intv : t) = function - | None -> intv - | Some (xs, range) -> - let excl_to_intervalset (ik: ikind) ((rl, rh): (int64 * int64)) (excl: int_t): t = - excl_range_to_intervalset ik (Ints_t.of_bigint (Size.min_from_bit_range rl),Ints_t.of_bigint (Size.max_from_bit_range rh)) excl - in - let excl_list = List.map (excl_to_intervalset ik range) xs in - List.fold_left (meet ik) intv excl_list - - let project ik p t = t - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let list_pair_arb = QCheck.small_list pair_arb in - let canonize_randomly_generated_list = (fun x -> norm_intvs ik x |> fst) in - let shrink xs = GobQCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list - in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) canonize_randomly_generated_list list_pair_arb) -end - -module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t = struct - include D - - let add ?no_ov ik x y = fst @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = fst @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = fst @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = fst @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = fst @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = fst @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = fst @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = fst @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = fst @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = fst @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = fst @@ D.shift_left ik x y - - let shift_right ik x y = fst @@ D.shift_right ik x y -end - -module IntIkind = struct let ikind () = Cil.IInt end -module Interval = IntervalFunctor (IntOps.BigIntOps) -module Interval32 = IntDomWithDefaultIkind (IntDomLifter (SOverflowUnlifter (IntervalFunctor (IntOps.Int64Ops)))) (IntIkind) -module IntervalSet = IntervalSetFunctor (IntOps.BigIntOps) -module Integers (Ints_t : IntOps.IntOps): IkindUnawareS with type t = Ints_t.t and type int_t = Ints_t.t = (* no top/bot, order is <= *) -struct - include Printable.Std - let name () = "integers" - type t = Ints_t.t [@@deriving eq, ord, hash] - type int_t = Ints_t.t - let top () = raise Unknown - let bot () = raise Error - let top_of ik = top () - let bot_of ik = bot () - let show (x: Ints_t.t) = Ints_t.to_string x - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - (* is_top and is_bot are never called, but if they were, the Std impl would raise their exception, so we overwrite them: *) - let is_top _ = false - let is_bot _ = false - - let equal_to i x = if i > x then `Neq else `Top - let leq x y = x <= y - let join x y = if Ints_t.compare x y > 0 then x else y - let widen = join - let meet x y = if Ints_t.compare x y > 0 then y else x - let narrow = meet - - let of_bool x = if x then Ints_t.one else Ints_t.zero - let to_bool' x = x <> Ints_t.zero - let to_bool x = Some (to_bool' x) - let of_int x = x - let to_int x = Some x - - let neg = Ints_t.neg - let add = Ints_t.add (* TODO: signed overflow is undefined behavior! *) - let sub = Ints_t.sub - let mul = Ints_t.mul - let div = Ints_t.div - let rem = Ints_t.rem - let lt n1 n2 = of_bool (n1 < n2) - let gt n1 n2 = of_bool (n1 > n2) - let le n1 n2 = of_bool (n1 <= n2) - let ge n1 n2 = of_bool (n1 >= n2) - let eq n1 n2 = of_bool (n1 = n2) - let ne n1 n2 = of_bool (n1 <> n2) - let lognot = Ints_t.lognot - let logand = Ints_t.logand - let logor = Ints_t.logor - let logxor = Ints_t.logxor - let shift_left n1 n2 = Ints_t.shift_left n1 (Ints_t.to_int n2) - let shift_right n1 n2 = Ints_t.shift_right n1 (Ints_t.to_int n2) - let c_lognot n1 = of_bool (not (to_bool' n1)) - let c_logand n1 n2 = of_bool ((to_bool' n1) && (to_bool' n2)) - let c_logor n1 n2 = of_bool ((to_bool' n1) || (to_bool' n2)) - let cast_to ?(suppress_ovwarn=false) ?torg t x = failwith @@ "Cast_to not implemented for " ^ (name ()) ^ "." - let arbitrary ik = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 (* TODO: use ikind *) - let invariant _ _ = Invariant.none (* TODO *) -end - -module FlatPureIntegers: IkindUnawareS with type t = int64 and type int_t = int64 = (* Integers, but raises Unknown/Error on join/meet *) -struct - include Integers(IntOps.Int64Ops) - let top () = raise Unknown - let bot () = raise Error - let leq = equal - let pretty_diff () (x,y) = Pretty.dprintf "Integer %a instead of %a" pretty x pretty y - let join x y = if equal x y then x else top () - let meet x y = if equal x y then x else bot () -end - -module Flat (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Lift, but goes to `Top/`Bot if Base raises Unknown/Error *) -struct - type int_t = Base.int_t - include Lattice.FlatConf (struct - include Printable.DefaultConf - let top_name = "Unknown int" - let bot_name = "Error int" - end) (Base) - - let top_of ik = top () - let bot_of ik = bot () - - - let name () = "flat integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ikind x = top_of ikind - let ending ?(suppress_ovwarn=false) ikind x = top_of ikind - let maximal x = None - let minimal x = None - - let lift1 f x = match x with - | `Lifted x -> - (try `Lifted (f x) with Unknown -> `Top | Error -> `Bot) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> - (try `Lifted (f x y) with Unknown -> `Top | Error -> `Bot) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Lift (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Flat, but does not go to `Top/Bot` if Base raises Unknown/Error *) -struct - include Lattice.LiftPO (struct - include Printable.DefaultConf - let top_name = "MaxInt" - let bot_name = "MinInt" - end) (Base) - type int_t = Base.int_t - let top_of ik = top () - let bot_of ik = bot () - include StdTop (struct type nonrec t = t let top_of = top_of end) - - let name () = "lifted integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let lift1 f x = match x with - | `Lifted x -> `Lifted (f x) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> `Lifted (f x y) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Flattened = Flat (Integers (IntOps.Int64Ops)) -module Lifted = Lift (Integers (IntOps.Int64Ops)) - -module Reverse (Base: IkindUnawareS) = -struct - include Base - include (Lattice.Reverse (Base) : Lattice.S with type t := Base.t) -end - -module BISet = struct - include SetDomain.Make (IntOps.BigIntOps) - let is_singleton s = cardinal s = 1 -end - -(* The module [Exclusion] constains common functionality about handling of exclusion sets between [DefExc] and [Enums] *) -module Exclusion = -struct - module R = Interval32 - (* We use these types for the functions in this module to make the intended meaning more explicit *) - type t = Exc of BISet.t * Interval32.t - type inc = Inc of BISet.t [@@unboxed] - let max_of_range r = Size.max_from_bit_range (Option.get (R.maximal r)) - let min_of_range r = Size.min_from_bit_range (Option.get (R.minimal r)) - let cardinality_of_range r = Z.succ (Z.add (Z.neg (min_of_range r)) (max_of_range r)) - - let cardinality_BISet s = - Z.of_int (BISet.cardinal s) - - let leq_excl_incl (Exc (xs, r)) (Inc ys) = - (* For a <= b to hold, the cardinalities must fit, i.e. |a| <= |b|, which implies |min_r, max_r| - |xs| <= |ys|. We check this first. *) - let lower_bound_cardinality_a = Z.sub (cardinality_of_range r) (cardinality_BISet xs) in - let card_b = cardinality_BISet ys in - if Z.compare lower_bound_cardinality_a card_b > 0 then - false - else (* The cardinality did fit, so we check for all elements that are represented by range r, whether they are in (xs union ys) *) - let min_a = min_of_range r in - let max_a = max_of_range r in - GobZ.for_all_range (fun el -> BISet.mem el xs || BISet.mem el ys) (min_a, max_a) - - let leq (Exc (xs, r)) (Exc (ys, s)) = - let min_a, max_a = min_of_range r, max_of_range r in - let excluded_check = BISet.for_all (fun y -> BISet.mem y xs || Z.compare y min_a < 0 || Z.compare y max_a > 0) ys in (* if true, then the values ys, that are not in b, also do not occur in a *) - if not excluded_check - then false - else begin (* Check whether all elements that are in the range r, but not in s, are in xs, i.e. excluded. *) - if R.leq r s then true - else begin if Z.compare (cardinality_BISet xs) (Z.sub (cardinality_of_range r) (cardinality_of_range s)) >= 0 (* Check whether the number of excluded elements in a is as least as big as |min_r, max_r| - |min_s, max_s| *) - then - let min_b, max_b = min_of_range s, max_of_range s in - let leq1 = (* check whether the elements in [r_l; s_l-1] are all in xs, i.e. excluded *) - if Z.compare min_a min_b < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (min_a, Z.pred min_b) - else - true - in - let leq2 () = (* check whether the elements in [s_u+1; r_u] are all in xs, i.e. excluded *) - if Z.compare max_b max_a < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (Z.succ max_b, max_a) - else - true - in - leq1 && (leq2 ()) - else - false - end - end -end - -module DefExc : S with type int_t = Z.t = (* definite or set of excluded values *) -struct - module S = BISet - module R = Interval32 (* range for exclusion *) - - (* Ikind used for intervals representing the domain *) - let range_ikind = Cil.IInt - let size t = R.of_interval range_ikind (let a,b = Size.bits_i64 t in Int64.neg a,b) - - - type t = [ - | `Excluded of S.t * R.t - | `Definite of Z.t - | `Bot - ] [@@deriving eq, ord, hash] - type int_t = Z.t - let name () = "def_exc" - - - let top_range = R.of_interval range_ikind (-99L, 99L) (* Since there is no top ikind we use a range that includes both ILongLong [-63,63] and IULongLong [0,64]. Only needed for intermediate range computation on longs. Correct range is set by cast. *) - let top () = `Excluded (S.empty (), top_range) - let bot () = `Bot - let top_of ik = `Excluded (S.empty (), size ik) - let bot_of ik = bot () - - let show x = - let short_size x = "("^R.show x^")" in - match x with - | `Bot -> "Error int" - | `Definite x -> Z.to_string x - (* Print the empty exclusion as if it was a distinct top element: *) - | `Excluded (s,l) when S.is_empty s -> "Unknown int" ^ short_size l - (* Prepend the exclusion sets with something: *) - | `Excluded (s,l) -> "Not " ^ S.show s ^ short_size l - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let maximal = function - | `Definite x -> Some x - | `Excluded (s,r) -> Some (Exclusion.max_of_range r) - | `Bot -> None - - let minimal = function - | `Definite x -> Some x - | `Excluded (s,r) -> Some (Exclusion.min_of_range r) - | `Bot -> None - - let in_range r i = - if Z.compare i Z.zero < 0 then - let lowerb = Exclusion.min_of_range r in - Z.compare lowerb i <= 0 - else - let upperb = Exclusion.max_of_range r in - Z.compare i upperb <= 0 - - let is_top x = x = top () - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Definite x -> if i = x then `Eq else `Neq - | `Excluded (s,r) -> if S.mem i s then `Neq else `Top - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik = function - | `Excluded (s,r) -> - let r' = size ik in - if R.leq r r' then (* upcast -> no change *) - `Excluded (s, r) - else if ik = IBool then (* downcast to bool *) - if S.mem Z.zero s then - `Definite Z.one - else - `Excluded (S.empty(), r') - else - (* downcast: may overflow *) - (* let s' = S.map (Size.cast ik) s in *) - (* We want to filter out all i in s' where (t)x with x in r could be i. *) - (* Since this is hard to compute, we just keep all i in s' which overflowed, since those are safe - all i which did not overflow may now be possible due to overflow of r. *) - (* S.diff s' s, r' *) - (* The above is needed for test 21/03, but not sound! See example https://github.com/goblint/analyzer/pull/95#discussion_r483023140 *) - `Excluded (S.empty (), r') - | `Definite x -> `Definite (Size.cast ik x) - | `Bot -> `Bot - - (* Wraps definite values and excluded values according to the ikind. - * For an `Excluded s,r , assumes that r is already an overapproximation of the range of possible values. - * r might be larger than the possible range of this type; the range of the returned `Excluded set will be within the bounds of the ikind. - *) - let norm ik v = - match v with - | `Excluded (s, r) -> - let possibly_overflowed = not (R.leq r (size ik)) || not (S.for_all (in_range (size ik)) s) in - (* If no overflow occurred, just return x *) - if not possibly_overflowed then ( - v - ) - (* Else, if an overflow might have occurred but we should just ignore it *) - else if should_ignore_overflow ik then ( - let r = size ik in - (* filter out excluded elements that are not in the range *) - let mapped_excl = S.filter (in_range r) s in - `Excluded (mapped_excl, r) - ) - (* Else, if an overflow occurred that we should not treat with wrap-around, go to top *) - else if not (should_wrap ik) then ( - top_of ik - ) else ( - (* Else an overflow occurred that we should treat with wrap-around *) - let r = size ik in - (* Perform a wrap-around for unsigned values and for signed values (if configured). *) - let mapped_excl = S.map (fun excl -> Size.cast ik excl) s in - match ik with - | IBool -> - begin match S.mem Z.zero mapped_excl, S.mem Z.one mapped_excl with - | false, false -> `Excluded (mapped_excl, r) (* Not {} -> Not {} *) - | true, false -> `Definite Z.one (* Not {0} -> 1 *) - | false, true -> `Definite Z.zero (* Not {1} -> 0 *) - | true, true -> `Bot (* Not {0, 1} -> bot *) - end - | ik -> - `Excluded (mapped_excl, r) - ) - | `Definite x -> - let min, max = Size.range ik in - (* Perform a wrap-around for unsigned values and for signed values (if configured). *) - if should_wrap ik then ( - cast_to ik v - ) - else if Z.compare min x <= 0 && Z.compare x max <= 0 then ( - v - ) - else if should_ignore_overflow ik then ( - M.warn ~category:M.Category.Integer.overflow "DefExc: Value was outside of range, indicating overflow, but 'sem.int.signed_overflow' is 'assume_none' -> Returned Bot"; - `Bot - ) - else ( - top_of ik - ) - | `Bot -> `Bot - - let leq x y = match (x,y) with - (* `Bot <= x is always true *) - | `Bot, _ -> true - (* Anything except bot <= bot is always false *) - | _, `Bot -> false - (* Two known values are leq whenever equal *) - | `Definite (x: int_t), `Definite y -> x = y - (* A definite value is leq all exclusion sets that don't contain it *) - | `Definite x, `Excluded (s,r) -> in_range r x && not (S.mem x s) - (* No finite exclusion set can be leq than a definite value *) - | `Excluded (xs, xr), `Definite d -> - Exclusion.(leq_excl_incl (Exc (xs, xr)) (Inc (S.singleton d))) - | `Excluded (xs,xr), `Excluded (ys,yr) -> - Exclusion.(leq (Exc (xs,xr)) (Exc (ys, yr))) - - let join' ?range ik x y = - match (x,y) with - (* The least upper bound with the bottom element: *) - | `Bot, x -> x - | x, `Bot -> x - (* The case for two known values: *) - | `Definite (x: int_t), `Definite y -> - (* If they're equal, it's just THAT value *) - if x = y then `Definite x - (* Unless one of them is zero, we can exclude it: *) - else - let a,b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval range_ikind a) (R.of_interval range_ikind b) in - `Excluded ((if Z.equal x Z.zero || Z.equal y Z.zero then S.empty () else S.singleton Z.zero), r) - (* A known value and an exclusion set... the definite value should no - * longer be excluded: *) - | `Excluded (s,r), `Definite x - | `Definite x, `Excluded (s,r) -> - if not (in_range r x) then - let a = R.of_interval range_ikind (Size.min_range_sign_agnostic x) in - `Excluded (S.remove x s, R.join a r) - else - `Excluded (S.remove x s, r) - (* For two exclusion sets, only their intersection can be excluded: *) - | `Excluded (x,wx), `Excluded (y,wy) -> `Excluded (S.inter x y, range |? R.join wx wy) - - let join ik = join' ik - - - let widen ik x y = - if get_def_exc_widen_by_join () then - join' ik x y - else if equal x y then - x - else - join' ~range:(size ik) ik x y - - - let meet ik x y = - match (x,y) with - (* Greatest LOWER bound with the least element is trivial: *) - | `Bot, _ -> `Bot - | _, `Bot -> `Bot - (* Definite elements are either equal or the glb is bottom *) - | `Definite x, `Definite y -> if x = y then `Definite x else `Bot - (* The glb of a definite element and an exclusion set is either bottom or - * just the element itself, if it isn't in the exclusion set *) - | `Excluded (s,r), `Definite x - | `Definite x, `Excluded (s,r) -> if S.mem x s || not (in_range r x) then `Bot else `Definite x - (* The greatest lower bound of two exclusion sets is their union, this is - * just DeMorgans Law *) - | `Excluded (x,r1), `Excluded (y,r2) -> - let r' = R.meet r1 r2 in - let s' = S.union x y |> S.filter (in_range r') in - `Excluded (s', r') - - let narrow ik x y = x - - let of_int ik x = norm ik @@ `Definite x - let to_int x = match x with - | `Definite x -> Some x - | _ -> None - - let from_excl ikind (s: S.t) = norm ikind @@ `Excluded (s, size ikind) - - let of_bool_cmp ik x = of_int ik (if x then Z.one else Z.zero) - let of_bool = of_bool_cmp - let to_bool x = - match x with - | `Definite x -> Some (IntOps.BigIntOps.to_bool x) - | `Excluded (s,r) when S.mem Z.zero s -> Some true - | _ -> None - let top_bool = `Excluded (S.empty (), R.of_interval range_ikind (0L, 1L)) - - let of_interval ?(suppress_ovwarn=false) ik (x,y) = - if Z.compare x y = 0 then - of_int ik x - else - let a, b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval ~suppress_ovwarn range_ikind a) (R.of_interval ~suppress_ovwarn range_ikind b) in - let ex = if Z.gt x Z.zero || Z.lt y Z.zero then S.singleton Z.zero else S.empty () in - norm ik @@ (`Excluded (ex, r)) - - let starting ?(suppress_ovwarn=false) ikind x = - let _,u_ik = Size.range ikind in - of_interval ~suppress_ovwarn ikind (x, u_ik) - - let ending ?(suppress_ovwarn=false) ikind x = - let l_ik,_ = Size.range ikind in - of_interval ~suppress_ovwarn ikind (l_ik, x) - - let of_excl_list t l = - let r = size t in (* elements in l are excluded from the full range of t! *) - `Excluded (List.fold_right S.add l (S.empty ()), r) - let is_excl_list l = match l with `Excluded _ -> true | _ -> false - let to_excl_list (x:t) = match x with - | `Definite _ -> None - | `Excluded (s,r) -> Some (S.elements s, (Option.get (R.minimal r), Option.get (R.maximal r))) - | `Bot -> None - - let to_incl_list x = match x with - | `Definite x -> Some [x] - | `Excluded _ -> None - | `Bot -> None - - let apply_range f r = (* apply f to the min/max of the old range r to get a new range *) - (* If the Int64 might overflow on us during computation, we instead go to top_range *) - match R.minimal r, R.maximal r with - | _ -> - let rf m = (size % Size.min_for % f) (m r) in - let r1, r2 = rf Exclusion.min_of_range, rf Exclusion.max_of_range in - R.join r1 r2 - - (* Default behaviour for unary operators, simply maps the function to the - * DefExc data structure. *) - let lift1 f ik x = norm ik @@ match x with - | `Excluded (s,r) -> - let s' = S.map f s in - `Excluded (s', apply_range f r) - | `Definite x -> `Definite (f x) - | `Bot -> `Bot - - let lift2 f ik x y = norm ik (match x,y with - (* We don't bother with exclusion sets: *) - | `Excluded _, `Definite _ - | `Definite _, `Excluded _ - | `Excluded _, `Excluded _ -> top () - (* The good case: *) - | `Definite x, `Definite y -> - (try `Definite (f x y) with | Division_by_zero -> top ()) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) - - (* Default behaviour for binary operators that are injective in either - * argument, so that Exclusion Sets can be used: *) - let lift2_inj f ik x y = - let def_exc f x s r = `Excluded (S.map (f x) s, apply_range (f x) r) in - norm ik @@ - match x,y with - (* If both are exclusion sets, there isn't anything we can do: *) - | `Excluded _, `Excluded _ -> top () - (* A definite value should be applied to all members of the exclusion set *) - | `Definite x, `Excluded (s,r) -> def_exc f x s r - (* Same thing here, but we should flip the operator to map it properly *) - | `Excluded (s,r), `Definite x -> def_exc (Batteries.flip f) x s r - (* The good case: *) - | `Definite x, `Definite y -> `Definite (f x y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - (* The equality check: *) - let eq ik x y = match x,y with - (* Not much to do with two exclusion sets: *) - | `Excluded _, `Excluded _ -> top () - (* Is x equal to an exclusion set, if it is a member then NO otherwise we - * don't know: *) - | `Definite x, `Excluded (s,r) -> if S.mem x s then of_bool IInt false else top () - | `Excluded (s,r), `Definite x -> if S.mem x s then of_bool IInt false else top () - (* The good case: *) - | `Definite x, `Definite y -> of_bool IInt (x = y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - (* The inequality check: *) - let ne ik x y = match x,y with - (* Not much to do with two exclusion sets: *) - | `Excluded _, `Excluded _ -> top () - (* Is x unequal to an exclusion set, if it is a member then Yes otherwise we - * don't know: *) - | `Definite x, `Excluded (s,r) -> if S.mem x s then of_bool IInt true else top () - | `Excluded (s,r), `Definite x -> if S.mem x s then of_bool IInt true else top () - (* The good case: *) - | `Definite x, `Definite y -> of_bool IInt (x <> y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - let neg ?no_ov ik (x :t) = norm ik @@ lift1 Z.neg ik x - let add ?no_ov ik x y = norm ik @@ lift2_inj Z.add ik x y - - let sub ?no_ov ik x y = norm ik @@ lift2_inj Z.sub ik x y - let mul ?no_ov ik x y = norm ik @@ match x, y with - | `Definite z, (`Excluded _ | `Definite _) when Z.equal z Z.zero -> x - | (`Excluded _ | `Definite _), `Definite z when Z.equal z Z.zero -> y - | `Definite a, `Excluded (s,r) - (* Integer multiplication with even numbers is not injective. *) - (* Thus we cannot exclude the values to which the exclusion set would be mapped to. *) - | `Excluded (s,r),`Definite a when Z.equal (Z.rem a (Z.of_int 2)) Z.zero -> `Excluded (S.empty (), apply_range (Z.mul a) r) - | _ -> lift2_inj Z.mul ik x y - let div ?no_ov ik x y = lift2 Z.div ik x y - let rem ik x y = lift2 Z.rem ik x y - - (* Comparison handling copied from Enums. *) - let handle_bot x y f = match x, y with - | `Bot, `Bot -> `Bot - | `Bot, _ - | _, `Bot -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> f () - - let lt ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 < 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 >= 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let gt ik x y = lt ik y x - - let le ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 <= 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 > 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let ge ik x y = le ik y x - - let lognot = lift1 Z.lognot - - let logand ik x y = norm ik (match x,y with - (* We don't bother with exclusion sets: *) - | `Excluded _, `Definite i -> - (* Except in two special cases *) - if Z.equal i Z.zero then - `Definite Z.zero - else if Z.equal i Z.one then - of_interval IBool (Z.zero, Z.one) - else - top () - | `Definite _, `Excluded _ - | `Excluded _, `Excluded _ -> top () - (* The good case: *) - | `Definite x, `Definite y -> - (try `Definite (Z.logand x y) with | Division_by_zero -> top ()) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) - - - let logor = lift2 Z.logor - let logxor = lift2 Z.logxor - - let shift (shift_op: int_t -> int -> int_t) (ik: Cil.ikind) (x: t) (y: t) = - (* BigInt only accepts int as second argument for shifts; perform conversion here *) - let shift_op_big_int a (b: int_t) = - let (b : int) = Z.to_int b in - shift_op a b - in - (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = GobOption.for_all (fun x -> Z.lt x Z.zero) in - if is_negative (minimal x) || is_negative (minimal y) then - top_of ik - else - norm ik @@ lift2 shift_op_big_int ik x y - - let shift_left = - shift Z.shift_left - - let shift_right = - shift Z.shift_right - (* TODO: lift does not treat Not {0} as true. *) - let c_logand ik x y = - match to_bool x, to_bool y with - | Some false, _ - | _, Some false -> - of_bool ik false - | _, _ -> - lift2 IntOps.BigIntOps.c_logand ik x y - let c_logor ik x y = - match to_bool x, to_bool y with - | Some true, _ - | _, Some true -> - of_bool ik true - | _, _ -> - lift2 IntOps.BigIntOps.c_logor ik x y - let c_lognot ik = eq ik (of_int ik Z.zero) - - let invariant_ikind e ik (x:t) = - match x with - | `Definite x -> - IntInvariant.of_int e ik x - | `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 ri = IntInvariant.of_interval e ik (rmin, rmax) in - let si = IntInvariant.of_excl_list e ik (S.elements s) in - Invariant.(ri && si) - | `Bot -> Invariant.none - - let arbitrary ik = - let open QCheck.Iter in - let excluded s = from_excl ik s in - let definite x = of_int ik x in - let shrink = function - | `Excluded (s, _) -> GobQCheck.shrink (S.arbitrary ()) s >|= excluded (* S TODO: possibly shrink excluded to definite *) - | `Definite x -> (return `Bot) <+> (GobQCheck.shrink (IntOps.BigIntOps.arbitrary ()) x >|= definite) - | `Bot -> empty - in - QCheck.frequency ~shrink ~print:show [ - 20, QCheck.map excluded (S.arbitrary ()); - 10, QCheck.map definite (IntOps.BigIntOps.arbitrary ()); - 1, QCheck.always `Bot - ] (* S TODO: decide frequencies *) - - let refine_with_congruence ik a b = a - let refine_with_interval ik a b = match a, b with - | x, Some(i) -> meet ik x (of_interval ik i) - | _ -> a - let refine_with_excl_list ik a b = match a, b with - | `Excluded (s, r), Some(ls, _) -> meet ik (`Excluded (s, r)) (of_excl_list ik ls) (* TODO: refine with excl range? *) - | _ -> a - let refine_with_incl_list ik a b = a - - let project ik p t = t -end - -(* Inclusion/Exclusion sets. Go to top on arithmetic operations (except for some easy cases, e.g. multiplication with 0). Joins on widen, i.e. precise integers as long as not derived from arithmetic expressions. *) -module Enums : S with type int_t = Z.t = struct - module R = Interval32 (* range for exclusion *) - - let range_ikind = Cil.IInt - let size t = R.of_interval range_ikind (let a,b = Size.bits_i64 t in Int64.neg a,b) - - type t = Inc of BISet.t | Exc of BISet.t * R.t [@@deriving eq, ord, hash] (* inclusion/exclusion set *) - - type int_t = Z.t - let name () = "enums" - let bot () = failwith "bot () not implemented for Enums" - let top () = failwith "top () not implemented for Enums" - let bot_of ik = Inc (BISet.empty ()) - let top_bool = Inc (BISet.of_list [Z.zero; Z.one]) - let top_of ik = - match ik with - | IBool -> top_bool - | _ -> Exc (BISet.empty (), size ik) - - let range ik = Size.range ik - -(* - let max_of_range r = Size.max_from_bit_range (Option.get (R.maximal r)) - let min_of_range r = Size.min_from_bit_range (Option.get (R.minimal r)) - let cardinality_of_range r = Z.add (Z.neg (min_of_range r)) (max_of_range r) *) - let value_in_range (min, max) v = Z.compare min v <= 0 && Z.compare v max <= 0 - - let show = function - | Inc xs when BISet.is_empty xs -> "bot" - | Inc xs -> "{" ^ (String.concat ", " (List.map Z.to_string (BISet.elements xs))) ^ "}" - | Exc (xs,r) -> "not {" ^ (String.concat ", " (List.map Z.to_string (BISet.elements xs))) ^ "} " ^ "("^R.show r^")" - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - (* Normalization function for enums, that handles overflows for Inc. - As we do not compute on Excl, we do not have to perform any overflow handling for it. *) - let norm ikind v = - let min, max = range ikind in - (* Whether the value v lies within the values of the specified ikind. *) - let value_in_ikind v = - Z.compare min v <= 0 && Z.compare v max <= 0 - in - match v with - | Inc xs when BISet.for_all value_in_ikind xs -> v - | Inc xs -> - if should_wrap ikind then - Inc (BISet.map (Size.cast ikind) xs) - else if should_ignore_overflow ikind then - Inc (BISet.filter value_in_ikind xs) - else - top_of ikind - | Exc (xs, r) -> - (* The following assert should hold for Exc, therefore we do not have to overflow handling / normalization for it: - let range_in_ikind r = - R.leq r (size ikind) - in - let r_min, r_max = min_of_range r, max_of_range r in - assert (range_in_ikind r && BISet.for_all (value_in_range (r_min, r_max)) xs); *) - begin match ikind with - | IBool -> - begin match BISet.mem Z.zero xs, BISet.mem Z.one xs with - | false, false -> top_bool (* Not {} -> {0, 1} *) - | true, false -> Inc (BISet.singleton Z.one) (* Not {0} -> {1} *) - | false, true -> Inc (BISet.singleton Z.zero) (* Not {1} -> {0} *) - | true, true -> bot_of ikind (* Not {0, 1} -> bot *) - end - | _ -> - v - end - - - let equal_to i = function - | Inc x -> - if BISet.mem i x then - if BISet.is_singleton x then `Eq - else `Top - else `Neq - | Exc (x, r) -> - if BISet.mem i x then `Neq - else `Top - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik v = norm ik @@ match v with - | Exc (s,r) -> - let r' = size ik in - if R.leq r r' then (* upcast -> no change *) - Exc (s, r) - else if ik = IBool then (* downcast to bool *) - if BISet.mem Z.zero s then - Inc (BISet.singleton Z.one) - else - Exc (BISet.empty(), r') - else (* downcast: may overflow *) - Exc ((BISet.empty ()), r') - | Inc xs -> - let casted_xs = BISet.map (Size.cast ik) xs in - if Cil.isSigned ik && not (BISet.equal xs casted_xs) - then top_of ik (* When casting into a signed type and the result does not fit, the behavior is implementation-defined *) - else Inc casted_xs - - let of_int ikind x = cast_to ikind (Inc (BISet.singleton x)) - - let of_interval ?(suppress_ovwarn=false) ik (x, y) = - if Z.compare x y = 0 then - of_int ik x - else - let a, b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval ~suppress_ovwarn range_ikind a) (R.of_interval ~suppress_ovwarn range_ikind b) in - let ex = if Z.gt x Z.zero || Z.lt y Z.zero then BISet.singleton Z.zero else BISet.empty () in - norm ik @@ (Exc (ex, r)) - - let join _ x y = - match x, y with - | Inc x, Inc y -> Inc (BISet.union x y) - | Exc (x,r1), Exc (y,r2) -> Exc (BISet.inter x y, R.join r1 r2) - | Exc (x,r), Inc y - | Inc y, Exc (x,r) -> - let r = if BISet.is_empty y - then r - else - let (min_el_range, max_el_range) = Batteries.Tuple2.mapn (fun x -> R.of_interval range_ikind (Size.min_range_sign_agnostic x)) (BISet.min_elt y, BISet.max_elt y) in - let range = R.join min_el_range max_el_range in - R.join r range - in - Exc (BISet.diff x y, r) - - let meet _ x y = - match x, y with - | Inc x, Inc y -> Inc (BISet.inter x y) - | Exc (x,r1), Exc (y,r2) -> - let r = R.meet r1 r2 in - let r_min, r_max = Exclusion.min_of_range r, Exclusion.max_of_range r in - let filter_by_range = BISet.filter (value_in_range (r_min, r_max)) in - (* We remove those elements from the exclusion set that do not fit in the range anyway *) - let excl = BISet.union (filter_by_range x) (filter_by_range y) in - Exc (excl, r) - | Inc x, Exc (y,r) - | Exc (y,r), Inc x -> Inc (BISet.diff x y) - - let widen = join - let narrow = meet - let leq a b = - match a, b with - | Inc xs, Exc (ys, r) -> - if BISet.is_empty xs - then true - else - let min_b, max_b = Exclusion.min_of_range r, Exclusion.max_of_range r in - let min_a, max_a = BISet.min_elt xs, BISet.max_elt xs in - (* Check that the xs fit into the range r *) - Z.compare min_b min_a <= 0 && Z.compare max_a max_b <= 0 && - (* && check that none of the values contained in xs is excluded, i.e. contained in ys. *) - BISet.for_all (fun x -> not (BISet.mem x ys)) xs - | Inc xs, Inc ys -> - BISet.subset xs ys - | Exc (xs, r), Exc (ys, s) -> - Exclusion.(leq (Exc (xs, r)) (Exc (ys, s))) - | Exc (xs, r), Inc ys -> - Exclusion.(leq_excl_incl (Exc (xs, r)) (Inc ys)) - - let handle_bot x y f = match is_bot x, is_bot y with - | false, false -> f () - | true, false - | false, true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | true, true -> Inc (BISet.empty ()) - - let lift1 f ikind v = norm ikind @@ match v with - | Inc x when BISet.is_empty x -> v (* Return bottom when value is bottom *) - | Inc x when BISet.is_singleton x -> Inc (BISet.singleton (f (BISet.choose x))) - | _ -> top_of ikind - - let lift2 f (ikind: Cil.ikind) u v = - handle_bot u v (fun () -> - norm ikind @@ match u, v with - | Inc x,Inc y when BISet.is_singleton x && BISet.is_singleton y -> Inc (BISet.singleton (f (BISet.choose x) (BISet.choose y))) - | _,_ -> top_of ikind) - - let lift2 f ikind a b = - try lift2 f ikind a b with Division_by_zero -> top_of ikind - - let neg ?no_ov = lift1 Z.neg - let add ?no_ov ikind a b = - match a, b with - | Inc z,x when BISet.is_singleton z && BISet.choose z = Z.zero -> x - | x,Inc z when BISet.is_singleton z && BISet.choose z = Z.zero -> x - | x,y -> lift2 Z.add ikind x y - let sub ?no_ov = lift2 Z.sub - let mul ?no_ov ikind a b = - match a, b with - | Inc one,x when BISet.is_singleton one && BISet.choose one = Z.one -> x - | x,Inc one when BISet.is_singleton one && BISet.choose one = Z.one -> x - | Inc zero,_ when BISet.is_singleton zero && BISet.choose zero = Z.zero -> a - | _,Inc zero when BISet.is_singleton zero && BISet.choose zero = Z.zero -> b - | x,y -> lift2 Z.mul ikind x y - - let div ?no_ov ikind a b = match a, b with - | x,Inc one when BISet.is_singleton one && BISet.choose one = Z.one -> x - | _,Inc zero when BISet.is_singleton zero && BISet.choose zero = Z.zero -> top_of ikind - | Inc zero,_ when BISet.is_singleton zero && BISet.choose zero = Z.zero -> a - | x,y -> lift2 Z.div ikind x y - - let rem = lift2 Z.rem - - let lognot = lift1 Z.lognot - let logand = lift2 Z.logand - let logor = lift2 Z.logor - let logxor = lift2 Z.logxor - - let shift (shift_op: int_t -> int -> int_t) (ik: Cil.ikind) (x: t) (y: t) = - handle_bot x y (fun () -> - (* BigInt only accepts int as second argument for shifts; perform conversion here *) - let shift_op_big_int a (b: int_t) = - let (b : int) = Z.to_int b in - shift_op a b - in - (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = GobOption.for_all (fun x -> Z.lt x Z.zero) in - if is_negative (minimal x) || is_negative (minimal y) then - top_of ik - else - lift2 shift_op_big_int ik x y) - - let shift_left = - shift Z.shift_left - - let shift_right = - shift Z.shift_right - - let of_bool ikind x = Inc (BISet.singleton (if x then Z.one else Z.zero)) - let to_bool = function - | Inc e when BISet.is_empty e -> None - | Exc (e,_) when BISet.is_empty e -> None - | Inc zero when BISet.is_singleton zero && BISet.choose zero = Z.zero -> Some false - | Inc xs when BISet.for_all ((<>) Z.zero) xs -> Some true - | Exc (xs,_) when BISet.exists ((=) Z.zero) xs -> Some true - | _ -> None - let to_int = function Inc x when BISet.is_singleton x -> Some (BISet.choose x) | _ -> None - - let to_excl_list = function Exc (x,r) when not (BISet.is_empty x) -> Some (BISet.elements x, (Option.get (R.minimal r), Option.get (R.maximal r))) | _ -> None - let of_excl_list ik xs = - let min_ik, max_ik = Size.range ik in - let exc = BISet.of_list @@ List.filter (value_in_range (min_ik, max_ik)) xs in - norm ik @@ Exc (exc, size ik) - let is_excl_list = BatOption.is_some % to_excl_list - let to_incl_list = function Inc s when not (BISet.is_empty s) -> Some (BISet.elements s) | _ -> None - - let starting ?(suppress_ovwarn=false) ikind x = - let _,u_ik = Size.range ikind in - of_interval ~suppress_ovwarn ikind (x, u_ik) - - let ending ?(suppress_ovwarn=false) ikind x = - let l_ik,_ = Size.range ikind in - of_interval ~suppress_ovwarn ikind (l_ik, x) - - let c_lognot ik x = - if is_bot x - then x - else - match to_bool x with - | Some b -> of_bool ik (not b) - | None -> top_bool - - let c_logand = lift2 IntOps.BigIntOps.c_logand - let c_logor = lift2 IntOps.BigIntOps.c_logor - let maximal = function - | Inc xs when not (BISet.is_empty xs) -> Some (BISet.max_elt xs) - | Exc (excl,r) -> - let rec decrement_while_contained v = - if BISet.mem v excl - then decrement_while_contained (Z.pred v) - else v - in - let range_max = Exclusion.max_of_range r in - Some (decrement_while_contained range_max) - | _ (* bottom case *) -> None - - let minimal = function - | Inc xs when not (BISet.is_empty xs) -> Some (BISet.min_elt xs) - | Exc (excl,r) -> - let rec increment_while_contained v = - if BISet.mem v excl - then increment_while_contained (Z.succ v) - else v - in - let range_min = Exclusion.min_of_range r in - Some (increment_while_contained range_min) - | _ (* bottom case *) -> None - - let lt ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 < 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 >= 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let gt ik x y = lt ik y x - - let le ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 <= 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 > 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let ge ik x y = le ik y x - - let eq ik x y = - handle_bot x y (fun () -> - match x, y with - | Inc xs, Inc ys when BISet.is_singleton xs && BISet.is_singleton ys -> of_bool ik (Z.equal (BISet.choose xs) (BISet.choose ys)) - | _, _ -> - if is_bot (meet ik x y) then - (* If the meet is empty, there is no chance that concrete values are equal *) - of_bool ik false - else - top_bool) - - let ne ik x y = c_lognot ik (eq ik x y) - - let invariant_ikind e ik x = - match x with - | Inc ps -> - IntInvariant.of_incl_list e ik (BISet.elements ps) - | Exc (ns, 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 ri = IntInvariant.of_interval e ik (rmin, rmax) in - let nsi = IntInvariant.of_excl_list e ik (BISet.elements ns) in - Invariant.(ri && nsi) - - - let arbitrary ik = - let open QCheck.Iter in - let neg s = of_excl_list ik (BISet.elements s) in - let pos s = norm ik (Inc s) in - let shrink = function - | Exc (s, _) -> GobQCheck.shrink (BISet.arbitrary ()) s >|= neg (* S TODO: possibly shrink neg to pos *) - | Inc s -> GobQCheck.shrink (BISet.arbitrary ()) s >|= pos - in - QCheck.frequency ~shrink ~print:show [ - 20, QCheck.map neg (BISet.arbitrary ()); - 10, QCheck.map pos (BISet.arbitrary ()); - ] (* S TODO: decide frequencies *) - - let refine_with_congruence ik a b = - let contains c m x = if Z.equal m Z.zero then Z.equal c x else Z.equal (Z.rem (Z.sub x c) m) Z.zero in - match a, b with - | Inc e, None -> bot_of ik - | Inc e, Some (c, m) -> Inc (BISet.filter (contains c m) e) - | _ -> a - - let refine_with_interval ik a b = a (* TODO: refine inclusion (exclusion?) set *) - - let refine_with_excl_list ik a b = - match b with - | Some (ls, _) -> meet ik a (of_excl_list ik ls) (* TODO: refine with excl range? *) - | _ -> a - - let refine_with_incl_list ik a b = - match a, b with - | Inc x, Some (ls) -> meet ik (Inc x) (Inc (BISet.of_list ls)) - | _ -> a - - let project ik p t = t -end - -module Congruence : S with type int_t = Z.t and type t = (Z.t * Z.t) option = -struct - let name () = "congruences" - type int_t = Z.t - - (* represents congruence class of c mod m, None is bot *) - type t = (Z.t * Z.t) option [@@deriving eq, ord, hash] - - let ( *: ) = Z.mul - let (+:) = Z.add - let (-:) = Z.sub - let (%:) = Z.rem - let (/:) = Z.div - let (=:) = Z.equal - let (<:) x y = Z.compare x y < 0 - let (>:) x y = Z.compare x y > 0 - let (<=:) x y = Z.compare x y <= 0 - let (>=:) x y = Z.compare x y >= 0 - (* a divides b *) - let ( |: ) a b = - if a =: Z.zero then false else (b %: a) =: Z.zero - - let normalize ik x = - match x with - | None -> None - | Some (c, m) -> - if m =: Z.zero then - if should_wrap ik then - Some (Size.cast ik c, m) - else - Some (c, m) - else - let m' = Z.abs m in - let c' = c %: m' in - if c' <: Z.zero then - Some (c' +: m', m') - else - Some (c' %: m', m') - - let range ik = Size.range ik - - let top () = Some (Z.zero, Z.one) - let top_of ik = Some (Z.zero, Z.one) - let bot () = None - let bot_of ik = bot () - - let show = function ik -> match ik with - | None -> "⟂" - | Some (c, m) when (c, m) = (Z.zero, Z.zero) -> Z.to_string c - | Some (c, m) -> - let a = if c =: Z.zero then "" else Z.to_string c in - let b = if m =: Z.zero then "" else if m = Z.one then "ℤ" else Z.to_string m^"ℤ" in - let c = if a = "" || b = "" then "" else "+" in - a^c^b - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let is_top x = x = top () - - let equal_to i = function - | None -> failwith "unsupported: equal_to with bottom" - | Some (a, b) when b =: Z.zero -> if a =: i then `Eq else `Neq - | Some (a, b) -> if i %: b =: a then `Top else `Neq - - let leq (x:t) (y:t) = - match x, y with - | None, _ -> true - | Some _, None -> false - | Some (c1,m1), Some (c2,m2) when m2 =: Z.zero && m1 =: Z.zero -> c1 =: c2 - | Some (c1,m1), Some (c2,m2) when m2 =: Z.zero -> c1 =: c2 && m1 =: Z.zero - | Some (c1,m1), Some (c2,m2) -> m2 |: Z.gcd (c1 -: c2) m1 - (* Typo in original equation of P. Granger (m2 instead of m1): gcd (c1 -: c2) m2 - Reference: https://doi.org/10.1080/00207168908803778 Page 171 corollary 3.3*) - - let leq x y = - let res = leq x y in - if M.tracing then M.trace "congruence" "leq %a %a -> %a " pretty x pretty y pretty (Some (Z.of_int (Bool.to_int res), Z.zero)) ; - res - - let join ik (x:t) y = - match x, y with - | None, z | z, None -> z - | Some (c1,m1), Some (c2,m2) -> - let m3 = Z.gcd m1 (Z.gcd m2 (c1 -: c2)) in - normalize ik (Some (c1, m3)) - - let join ik (x:t) y = - let res = join ik x y in - if M.tracing then M.trace "congruence" "join %a %a -> %a" pretty x pretty y pretty res; - res - - - let meet ik x y = - (* if it exists, c2/a2 is solution to a*x ≡ c (mod m) *) - let congruence_series a c m = - let rec next a1 c1 a2 c2 = - if a2 |: a1 then (a2, c2) - else next a2 c2 (a1 %: a2) (c1 -: (c2 *: (a1 /: a2))) - in next m Z.zero a c - in - let simple_case i c m = - if m |: (i -: c) - then Some (i, Z.zero) else None - in - match x, y with - | Some (c1, m1), Some (c2, m2) when m1 =: Z.zero && m2 =: Z.zero -> if c1 =: c2 then Some (c1, Z.zero) else None - | Some (c1, m1), Some (c2, m2) when m1 =: Z.zero -> simple_case c1 c2 m2 - | Some (c1, m1), Some (c2, m2) when m2 =: Z.zero -> simple_case c2 c1 m1 - | Some (c1, m1), Some (c2, m2) when (Z.gcd m1 m2) |: (c1 -: c2) -> - let (c, m) = congruence_series m1 (c2 -: c1 ) m2 in - normalize ik (Some(c1 +: (m1 *: (m /: c)), m1 *: (m2 /: c))) - | _ -> None - - let meet ik x y = - let res = meet ik x y in - if M.tracing then M.trace "congruence" "meet %a %a -> %a" pretty x pretty y pretty res; - res - - let to_int = function Some (c, m) when m =: Z.zero -> Some c | _ -> None - let of_int ik (x: int_t) = normalize ik @@ Some (x, Z.zero) - let zero = Some (Z.zero, Z.zero) - let one = Some (Z.one, Z.zero) - let top_bool = top() - - let of_bool _ik = function true -> one | false -> zero - - let to_bool (a: t) = match a with - | None -> None - | x when equal zero x -> Some false - | x -> if leq zero x then None else Some true - - let starting ?(suppress_ovwarn=false) ik n = top() - - let ending = starting - - let of_congruence ik (c,m) = normalize ik @@ Some(c,m) - - let maximal t = match t with - | Some (x, y) when y =: Z.zero -> Some x - | _ -> None - - let minimal t = match t with - | Some (x,y) when y =: Z.zero -> Some x - | _ -> None - - (* cast from original type to ikind, set to top if the value doesn't fit into the new type *) - let cast_to ?(suppress_ovwarn=false) ?torg ?(no_ov=false) t x = - match x with - | None -> None - | Some (c, m) when m =: Z.zero -> - let c' = Size.cast t c in - (* When casting into a signed type and the result does not fit, the behavior is implementation-defined. (C90 6.2.1.2, C99 and C11 6.3.1.3) *) - (* We go with GCC behavior here: *) - (* For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised. *) - (* (https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html) *) - (* Clang behaves the same but they never document that anywhere *) - Some (c', m) - | _ -> - let (min_t, max_t) = range t in - let p ikorg = - let (min_ikorg, max_ikorg) = range ikorg in - ikorg = t || (max_t >=: max_ikorg && min_t <=: min_ikorg) - in - match torg with - | Some (Cil.TInt (ikorg, _)) when p ikorg -> - if M.tracing then M.trace "cong-cast" "some case"; - x - | _ -> top () - - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov (t : Cil.ikind) x = - let pretty_bool _ x = Pretty.text (string_of_bool x) in - let res = cast_to ?torg ?no_ov t x in - if M.tracing then M.trace "cong-cast" "Cast %a to %a (no_ov: %a) = %a" pretty x Cil.d_ikind t (Pretty.docOpt (pretty_bool ())) no_ov pretty res; - res - - let widen = join - - let widen ik x y = - let res = widen ik x y in - if M.tracing then M.trace "congruence" "widen %a %a -> %a" pretty x pretty y pretty res; - res - - let narrow = meet - - let log f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_bool i1, to_bool i2 with - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - let c_logor = log (||) - let c_logand = log (&&) - - let log1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_bool i1 with - | Some x -> of_bool ik (f ik x) - | _ -> top_of ik - - let c_lognot = log1 (fun _ik -> not) - - let shift_right _ _ _ = top() - - let shift_right ik x y = - let res = shift_right ik x y in - if M.tracing then M.trace "congruence" "shift_right : %a %a becomes %a " pretty x pretty y pretty res; - res - - let shift_left ik x y = - (* Naive primality test *) - (* let is_prime n = - let n = Z.abs n in - let rec is_prime' d = - (d *: d >: n) || ((not ((n %: d) =: Z.zero)) && (is_prime' [@tailcall]) (d +: Z.one)) - in - not (n =: Z.one) && is_prime' (Z.of_int 2) - in *) - match x, y with - | None, None -> None - | None, _ - | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c, m), Some (c', m') when Cil.isSigned ik || c <: Z.zero || c' <: Z.zero -> top_of ik - | Some (c, m), Some (c', m') -> - let (_, max_ik) = range ik in - if m =: Z.zero && m' =: Z.zero then - normalize ik @@ Some (Z.logand max_ik (Z.shift_left c (Z.to_int c')), Z.zero) - else - let x = Z.logand max_ik (Z.shift_left Z.one (Z.to_int c')) in (* 2^c' *) - (* TODO: commented out because fails test with _Bool *) - (* if is_prime (m' +: Z.one) then - normalize ik @@ Some (x *: c, Z.gcd (x *: m) ((c *: x) *: (m' +: Z.one))) - else *) - normalize ik @@ Some (x *: c, Z.gcd (x *: m) (c *: x)) - - let shift_left ik x y = - let res = shift_left ik x y in - if M.tracing then M.trace "congruence" "shift_left : %a %a becomes %a " pretty x pretty y pretty res; - res - - (* Handle unsigned overflows. - From n === k mod (2^a * b), we conclude n === k mod 2^a, for a <= bitwidth. - The congruence modulo b may not persist on an overflow. *) - let handle_overflow ik (c, m) = - if m =: Z.zero then - normalize ik (Some (c, m)) - else - (* Find largest m'=2^k (for some k) such that m is divisible by m' *) - let tz = Z.trailing_zeros m in - let m' = Z.shift_left Z.one tz in - - let max = (snd (Size.range ik)) +: Z.one in - if m' >=: max then - (* if m' >= 2 ^ {bitlength}, there is only one value in range *) - let c' = c %: max in - Some (c', Z.zero) - else - normalize ik (Some (c, m')) - - let mul ?(no_ov=false) ik x y = - let no_ov_case (c1, m1) (c2, m2) = - c1 *: c2, Z.gcd (c1 *: m2) (Z.gcd (m1 *: c2) (m1 *: m2)) - in - match x, y with - | None, None -> bot () - | None, _ | _, None -> - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c1, m1), Some (c2, m2) when no_ov -> - Some (no_ov_case (c1, m1) (c2, m2)) - | Some (c1, m1), Some (c2, m2) when m1 =: Z.zero && m2 =: Z.zero && not (Cil.isSigned ik) -> - let (_, max_ik) = range ik in - Some ((c1 *: c2) %: (max_ik +: Z.one), Z.zero) - | Some a, Some b when not (Cil.isSigned ik) -> - handle_overflow ik (no_ov_case a b ) - | _ -> top () - - let mul ?no_ov ik x y = - let res = mul ?no_ov ik x y in - if M.tracing then M.trace "congruence" "mul : %a %a -> %a " pretty x pretty y pretty res; - res - - let neg ?(no_ov=false) ik x = - match x with - | None -> bot() - | Some _ -> mul ~no_ov ik (of_int ik (Z.of_int (-1))) x - - let add ?(no_ov=false) ik x y = - let no_ov_case (c1, m1) (c2, m2) = - c1 +: c2, Z.gcd m1 m2 - in - match (x, y) with - | None, None -> bot () - | None, _ | _, None -> - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some a, Some b when no_ov -> - normalize ik (Some (no_ov_case a b)) - | Some (c1, m1), Some (c2, m2) when m1 =: Z.zero && m2 =: Z.zero && not (Cil.isSigned ik) -> - let (_, max_ik) = range ik in - Some((c1 +: c2) %: (max_ik +: Z.one), Z.zero) - | Some a, Some b when not (Cil.isSigned ik) -> - handle_overflow ik (no_ov_case a b) - | _ -> top () - - - let add ?no_ov ik x y = - let res = add ?no_ov ik x y in - if M.tracing then - M.trace "congruence" "add : %a %a -> %a" pretty x pretty y - pretty res ; - res - - let sub ?(no_ov=false) ik x y = add ~no_ov ik x (neg ~no_ov ik y) - - - let sub ?no_ov ik x y = - let res = sub ?no_ov ik x y in - if M.tracing then - M.trace "congruence" "sub : %a %a -> %a" pretty x pretty y - pretty res ; - res - - let lognot ik x = match x with - | None -> None - | Some (c, m) -> - if (Cil.isSigned ik) then - sub ik (neg ik x) one - else - let (_, max_ik) = range ik in - Some (Z.sub max_ik c, m) - - (** The implementation of the bit operations could be improved based on the master’s thesis - 'Abstract Interpretation and Abstract Domains' written by Stefan Bygde. - see: http://www.es.mdh.se/pdf_publications/948.pdf *) - let bit2 f ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c, m), Some (c', m') -> - if m =: Z.zero && m' =: Z.zero then Some (f c c', Z.zero) - else top () - - let logor ik x y = bit2 Z.logor ik x y - - let logand ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c, m), Some (c', m') -> - if m =: Z.zero && m' =: Z.zero then - (* both arguments constant *) - Some (Z.logand c c', Z.zero) - else if m' =: Z.zero && c' =: Z.one && Z.rem m (Z.of_int 2) =: Z.zero then - (* x & 1 and x == c (mod 2*z) *) - (* Value is equal to LSB of c *) - Some (Z.logand c c', Z.zero) - else - top () - - let logxor ik x y = bit2 Z.logxor ik x y - - let rem ik x y = - match x, y with - | None, None -> bot() - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c1, m1), Some(c2, m2) -> - if m2 =: Z.zero then - if (c2 |: m1) && (c1 %: c2 =: Z.zero || m1 =: Z.zero || not (Cil.isSigned ik)) then - Some (c1 %: c2, Z.zero) - else - normalize ik (Some (c1, (Z.gcd m1 c2))) - else - normalize ik (Some (c1, Z.gcd m1 (Z.gcd c2 m2))) - - let rem ik x y = let res = rem ik x y in - if M.tracing then M.trace "congruence" "rem : %a %a -> %a " pretty x pretty y pretty res; - res - - let div ?(no_ov=false) ik x y = - match x,y with - | None, None -> bot () - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, x when leq zero x -> top () - | Some(c1, m1), Some(c2, m2) when not no_ov && m2 =: Z.zero && c2 =: Z.neg Z.one -> top () - | Some(c1, m1), Some(c2, m2) when m1 =: Z.zero && m2 =: Z.zero -> Some (c1 /: c2, Z.zero) - | Some(c1, m1), Some(c2, m2) when m2 =: Z.zero && c2 |: m1 && c2 |: c1 -> Some (c1 /: c2, m1 /: c2) - | _, _ -> top () - - - let div ?no_ov ik x y = - let res = div ?no_ov ik x y in - if M.tracing then - M.trace "congruence" "div : %a %a -> %a" pretty x pretty y pretty - res ; - res - - let ne ik (x: t) (y: t) = match x, y with - | Some (c1, m1), Some (c2, m2) when (m1 =: Z.zero) && (m2 =: Z.zero) -> of_bool ik (not (c1 =: c2 )) - | x, y -> if meet ik x y = None then of_bool ik true else top_bool - - let eq ik (x: t) (y: t) = match x, y with - | Some (c1, m1), Some (c2, m2) when (m1 =: Z.zero) && (m2 =: Z.zero) -> of_bool ik (c1 =: c2) - | x, y -> if meet ik x y <> None then top_bool else of_bool ik false - - let comparison ik op x y = match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c1, m1), Some (c2, m2) -> - if m1 =: Z.zero && m2 =: Z.zero then - if op c1 c2 then of_bool ik true else of_bool ik false - else - top_bool - - let ge ik x y = comparison ik (>=:) x y - - let ge ik x y = - let res = ge ik x y in - if M.tracing then M.trace "congruence" "greater or equal : %a %a -> %a " pretty x pretty y pretty res; - res - - let le ik x y = comparison ik (<=:) x y - - let le ik x y = - let res = le ik x y in - if M.tracing then M.trace "congruence" "less or equal : %a %a -> %a " pretty x pretty y pretty res; - res - - let gt ik x y = comparison ik (>:) x y - - - let gt ik x y = - let res = gt ik x y in - if M.tracing then M.trace "congruence" "greater than : %a %a -> %a " pretty x pretty y pretty res; - res - - let lt ik x y = comparison ik (<:) x y - - let lt ik x y = - let res = lt ik x y in - if M.tracing then M.trace "congruence" "less than : %a %a -> %a " pretty x pretty y pretty res; - res - - let invariant_ikind e ik x = - match x with - | x when is_top x -> Invariant.top () - | Some (c, m) when m =: Z.zero -> - IntInvariant.of_int e ik c - | Some (c, m) -> - let open Cil in - let (c, m) = BatTuple.Tuple2.mapn (fun a -> kintegerCilint ik a) (c, m) in - Invariant.of_exp (BinOp (Eq, (BinOp (Mod, e, m, TInt(ik,[]))), c, intType)) - | None -> Invariant.none - - let arbitrary ik = - let open QCheck in - let int_arb = map ~rev:Z.to_int64 Z.of_int64 GobQCheck.Arbitrary.int64 in - let cong_arb = pair int_arb int_arb in - let of_pair ik p = normalize ik (Some p) in - let to_pair = Option.get in - set_print show (map ~rev:to_pair (of_pair ik) cong_arb) - - let refine_with_interval ik (cong : t) (intv : (int_t * int_t ) option) : t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if m =: Z.zero then - if c <: x || c >: y then None else Some (c, Z.zero) - else - let rcx = x +: ((c -: x) %: Z.abs m) in - let lcy = y -: ((y -: c) %: Z.abs m) in - if rcx >: lcy then None - else if rcx =: lcy then Some (rcx, Z.zero) - else cong - | _ -> None - - let refine_with_interval ik (cong : t) (intv : (int_t * int_t) option) : t = - let pretty_intv _ i = - match i with - | Some (l, u) -> Pretty.dprintf "[%a,%a]" GobZ.pretty l GobZ.pretty u - | _ -> Pretty.text ("Display Error") in - let refn = refine_with_interval ik cong intv in - if M.tracing then M.trace "refine" "cong_refine_with_interval %a %a -> %a" pretty cong pretty_intv intv pretty refn; - refn - - let refine_with_congruence ik a b = meet ik a b - let refine_with_excl_list ik a b = a - let refine_with_incl_list ik a b = a - - let project ik p t = t -end - -module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct - - include D - - let lift v = (v, {overflow=false; underflow=false}) - - let add ?no_ov ik x y = lift @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = lift @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = lift @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = lift @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = lift @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = lift @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = lift @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = lift @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = lift @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = lift @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = lift @@ D.shift_left ik x y - - let shift_right ik x y = lift @@ D.shift_right ik x y - -end - +open IntDomain0 (* The old IntDomList had too much boilerplate since we had to edit every function in S when adding a new domain. With the following, we only have to edit the places where fn are applied, i.e., create, mapp, map, map2. You can search for I3 below to see where you need to extend. *) From 816809fcfcf7fb6fab2c79d43fe47940f0a9af91 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 16:49:35 +0300 Subject: [PATCH 460/566] Remove IntDomTuple from IntDomain0 --- src/cdomain/value/cdomains/intDomain0.ml | 516 ----------------------- 1 file changed, 516 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/intDomain0.ml index f4639d4522..7450e8a212 100644 --- a/src/cdomain/value/cdomains/intDomain0.ml +++ b/src/cdomain/value/cdomains/intDomain0.ml @@ -3267,519 +3267,3 @@ module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t let shift_right ik x y = lift @@ D.shift_right ik x y end - - - -(* The old IntDomList had too much boilerplate since we had to edit every function in S when adding a new domain. With the following, we only have to edit the places where fn are applied, i.e., create, mapp, map, map2. You can search for I3 below to see where you need to extend. *) -(* discussion: https://github.com/goblint/analyzer/pull/188#issuecomment-818928540 *) -module IntDomTupleImpl = struct - include Printable.Std (* for default invariant, tag, ... *) - - open Batteries - type int_t = Z.t - module I1 = SOverflowLifter (DefExc) - module I2 = Interval - module I3 = SOverflowLifter (Enums) - module I4 = SOverflowLifter (Congruence) - module I5 = IntervalSetFunctor (IntOps.BigIntOps) - - type t = I1.t option * I2.t option * I3.t option * I4.t option * I5.t option - [@@deriving eq, ord, hash] - - let name () = "intdomtuple" - - (* The Interval domain can lead to too many contexts for recursive functions (top is [min,max]), but we don't want to drop all ints as with `ana.base.context.int`. TODO better solution? *) - let no_interval = Tuple5.map2 (const None) - let no_intervalSet = Tuple5.map5 (const None) - - type 'a m = (module SOverflow with type t = 'a) - type 'a m2 = (module SOverflow with type t = 'a and type int_t = int_t ) - - (* only first-order polymorphism on functions -> use records to get around monomorphism restriction on arguments *) - type 'b poly_in = { fi : 'a. 'a m -> 'b -> 'a } [@@unboxed] (* inject *) - type 'b poly2_in = { fi2 : 'a. 'a m2 -> 'b -> 'a } [@@unboxed] (* inject for functions that depend on int_t *) - type 'b poly2_in_ovc = { fi2_ovc : 'a. 'a m2 -> 'b -> 'a * overflow_info} [@@unboxed] (* inject for functions that depend on int_t *) - - type 'b poly_pr = { fp : 'a. 'a m -> 'a -> 'b } [@@unboxed] (* project *) - type 'b poly_pr2 = { fp2 : 'a. 'a m2 -> 'a -> 'b } [@@unboxed] (* project for functions that depend on int_t *) - type 'b poly2_pr = {f2p: 'a. 'a m -> ?no_ov:bool -> 'a -> 'a -> 'b} [@@unboxed] - type poly1 = {f1: 'a. 'a m -> ?no_ov:bool -> 'a -> 'a} [@@unboxed] (* needed b/c above 'b must be different from 'a *) - type poly1_ovc = {f1_ovc: 'a. 'a m -> ?no_ov:bool -> 'a -> 'a * overflow_info } [@@unboxed] (* needed b/c above 'b must be different from 'a *) - type poly2 = {f2: 'a. 'a m -> ?no_ov:bool -> 'a -> 'a -> 'a} [@@unboxed] - type poly2_ovc = {f2_ovc: 'a. 'a m -> ?no_ov:bool -> 'a -> 'a -> 'a * overflow_info } [@@unboxed] - type 'b poly3 = { f3: 'a. 'a m -> 'a option } [@@unboxed] (* used for projection to given precision *) - let create r x ((p1, p2, p3, p4, p5): int_precision) = - let f b g = if b then Some (g x) else None in - f p1 @@ r.fi (module I1), f p2 @@ r.fi (module I2), f p3 @@ r.fi (module I3), f p4 @@ r.fi (module I4), f p5 @@ r.fi (module I5) - let create r x = (* use where values are introduced *) - create r x (int_precision_from_node_or_config ()) - let create2 r x ((p1, p2, p3, p4, p5): int_precision) = - let f b g = if b then Some (g x) else None in - f p1 @@ r.fi2 (module I1), f p2 @@ r.fi2 (module I2), f p3 @@ r.fi2 (module I3), f p4 @@ r.fi2 (module I4), f p5 @@ r.fi2 (module I5) - let create2 r x = (* use where values are introduced *) - create2 r x (int_precision_from_node_or_config ()) - - let no_overflow ik = function - | Some(_, {underflow; overflow}) -> not (underflow || overflow) - | _ -> false - - let check_ov ?(suppress_ovwarn = false) ~cast ik intv intv_set = - let no_ov = (no_overflow ik intv) || (no_overflow ik intv_set) in - if not no_ov && not suppress_ovwarn && ( BatOption.is_some intv || BatOption.is_some intv_set) then ( - let (_,{underflow=underflow_intv; overflow=overflow_intv}) = match intv with None -> (I2.bot (), {underflow= true; overflow = true}) | Some x -> x in - let (_,{underflow=underflow_intv_set; overflow=overflow_intv_set}) = match intv_set with None -> (I5.bot (), {underflow= true; overflow = true}) | Some x -> x in - let underflow = underflow_intv && underflow_intv_set in - let overflow = overflow_intv && overflow_intv_set in - set_overflow_flag ~cast ~underflow ~overflow ik; - ); - no_ov - - let create2_ovc ik r x ((p1, p2, p3, p4, p5): int_precision) = - let f b g = if b then Some (g x) else None in - let map x = Option.map fst x in - let intv = f p2 @@ r.fi2_ovc (module I2) in - let intv_set = f p5 @@ r.fi2_ovc (module I5) in - ignore (check_ov ~cast:false ik intv intv_set); - map @@ f p1 @@ r.fi2_ovc (module I1), map @@ f p2 @@ r.fi2_ovc (module I2), map @@ f p3 @@ r.fi2_ovc (module I3), map @@ f p4 @@ r.fi2_ovc (module I4), map @@ f p5 @@ r.fi2_ovc (module I5) - - let create2_ovc ik r x = (* use where values are introduced *) - create2_ovc ik r x (int_precision_from_node_or_config ()) - - - let opt_map2 f ?no_ov = - curry @@ function Some x, Some y -> Some (f ?no_ov x y) | _ -> None - - let to_list x = Tuple5.enum x |> List.of_enum |> List.filter_map identity (* contains only the values of activated domains *) - let to_list_some x = List.filter_map identity @@ to_list x (* contains only the Some-values of activated domains *) - - let exists = function - | (Some true, _, _, _, _) - | (_, Some true, _, _, _) - | (_, _, Some true, _, _) - | (_, _, _, Some true, _) - | (_, _, _, _, Some true) -> - true - | _ -> - false - - let for_all = function - | (Some false, _, _, _, _) - | (_, Some false, _, _, _) - | (_, _, Some false, _, _) - | (_, _, _, Some false, _) - | (_, _, _, _, Some false) -> - false - | _ -> - true - - (* f0: constructors *) - let top () = create { fi = fun (type a) (module I:SOverflow with type t = a) -> I.top } () - let bot () = create { fi = fun (type a) (module I:SOverflow with type t = a) -> I.bot } () - let top_of = create { fi = fun (type a) (module I:SOverflow with type t = a) -> I.top_of } - let bot_of = create { fi = fun (type a) (module I:SOverflow with type t = a) -> I.bot_of } - let of_bool ik = create { fi = fun (type a) (module I:SOverflow with type t = a) -> I.of_bool ik } - let of_excl_list ik = create2 { fi2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.of_excl_list ik} - let of_int ik = create2_ovc ik { fi2_ovc = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.of_int ik } - let starting ?(suppress_ovwarn=false) ik = create2_ovc ik { fi2_ovc = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.starting ~suppress_ovwarn ik } - let ending ?(suppress_ovwarn=false) ik = create2_ovc ik { fi2_ovc = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.ending ~suppress_ovwarn ik } - let of_interval ?(suppress_ovwarn=false) ik = create2_ovc ik { fi2_ovc = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.of_interval ~suppress_ovwarn ik } - let of_congruence ik = create2 { fi2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.of_congruence ik } - - let refine_with_congruence ik ((a, b, c, d, e) : t) (cong : (int_t * int_t) option) : t= - let opt f a = - curry @@ function Some x, y -> Some (f a x y) | _ -> None - in - ( opt I1.refine_with_congruence ik a cong - , opt I2.refine_with_congruence ik b cong - , opt I3.refine_with_congruence ik c cong - , opt I4.refine_with_congruence ik d cong - , opt I5.refine_with_congruence ik e cong) - - let refine_with_interval ik (a, b, c, d, e) intv = - let opt f a = - curry @@ function Some x, y -> Some (f a x y) | _ -> None - in - ( opt I1.refine_with_interval ik a intv - , opt I2.refine_with_interval ik b intv - , opt I3.refine_with_interval ik c intv - , opt I4.refine_with_interval ik d intv - , opt I5.refine_with_interval ik e intv ) - - let refine_with_excl_list ik (a, b, c, d, e) excl = - let opt f a = - curry @@ function Some x, y -> Some (f a x y) | _ -> None - in - ( opt I1.refine_with_excl_list ik a excl - , opt I2.refine_with_excl_list ik b excl - , opt I3.refine_with_excl_list ik c excl - , opt I4.refine_with_excl_list ik d excl - , opt I5.refine_with_excl_list ik e excl ) - - let refine_with_incl_list ik (a, b, c, d, e) incl = - let opt f a = - curry @@ function Some x, y -> Some (f a x y) | _ -> None - in - ( opt I1.refine_with_incl_list ik a incl - , opt I2.refine_with_incl_list ik b incl - , opt I3.refine_with_incl_list ik c incl - , opt I4.refine_with_incl_list ik d incl - , opt I5.refine_with_incl_list ik e incl ) - - - let mapp r (a, b, c, d, e) = - let map = BatOption.map in - ( map (r.fp (module I1)) a - , map (r.fp (module I2)) b - , map (r.fp (module I3)) c - , map (r.fp (module I4)) d - , map (r.fp (module I5)) e) - - - let mapp2 r (a, b, c, d, e) = - BatOption. - ( map (r.fp2 (module I1)) a - , map (r.fp2 (module I2)) b - , map (r.fp2 (module I3)) c - , map (r.fp2 (module I4)) d - , map (r.fp2 (module I5)) e) - - - (* exists/for_all *) - let is_bot = exists % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.is_bot } - let is_top = for_all % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.is_top } - let is_top_of ik = for_all % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.is_top_of ik } - let is_excl_list = exists % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.is_excl_list } - - let map2p r (xa, xb, xc, xd, xe) (ya, yb, yc, yd, ye) = - ( opt_map2 (r.f2p (module I1)) xa ya - , opt_map2 (r.f2p (module I2)) xb yb - , opt_map2 (r.f2p (module I3)) xc yc - , opt_map2 (r.f2p (module I4)) xd yd - , opt_map2 (r.f2p (module I5)) xe ye) - - (* f2p: binary projections *) - let (%%) f g x = f % (g x) (* composition for binary function g *) - - let leq = - for_all - %% map2p {f2p= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.leq)} - - let flat f x = match to_list_some x with [] -> None | xs -> Some (f xs) - - let to_excl_list x = - let merge ps = - let (vs, rs) = List.split ps in - let (mins, maxs) = List.split rs in - (List.concat vs |> List.sort_uniq Z.compare, (List.min mins, List.max maxs)) - in - mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.to_excl_list } x |> flat merge - - let to_incl_list x = - let hd l = match l with h::t -> h | _ -> [] in - let tl l = match l with h::t -> t | _ -> [] in - let a y = BatSet.of_list (hd y) in - let b y = BatList.map BatSet.of_list (tl y) in - let merge y = BatSet.elements @@ BatList.fold BatSet.intersect (a y) (b y) - in - mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.to_incl_list } x |> flat merge - - let same show x = let xs = to_list_some x in let us = List.unique xs in let n = List.length us in - if n = 1 then Some (List.hd xs) - else ( - if n>1 then Messages.info ~category:Unsound "Inconsistent state! %a" (Pretty.docList ~sep:(Pretty.text ",") (Pretty.text % show)) us; (* do not want to abort *) - None - ) - let to_int = same Z.to_string % mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.to_int } - - let pretty () x = - match to_int x with - | Some v when not (GobConfig.get_bool "dbg.full-output") -> Pretty.text (Z.to_string v) - | _ -> - mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> (* assert sf==I.short; *) I.pretty () } x - |> to_list - |> (fun xs -> - text "(" ++ ( - try - List.reduce (fun a b -> a ++ text "," ++ b) xs - with Invalid_argument _ -> - nil) - ++ text ")") (* NOTE: the version above does something else. also, we ignore the sf-argument here. *) - - let refine_functions ik : (t -> t) list = - let maybe reffun ik domtup dom = - match dom with Some y -> reffun ik domtup y | _ -> domtup - in - [(fun (a, b, c, d, e) -> refine_with_excl_list ik (a, b, c, d, e) (to_excl_list (a, b, c, d, e))); - (fun (a, b, c, d, e) -> refine_with_incl_list ik (a, b, c, d, e) (to_incl_list (a, b, c, d, e))); - (fun (a, b, c, d, e) -> maybe refine_with_interval ik (a, b, c, d, e) b); (* TODO: get interval across all domains with minimal and maximal *) - (fun (a, b, c, d, e) -> maybe refine_with_congruence ik (a, b, c, d, e) d)] - - let refine ik ((a, b, c, d, e) : t ) : t = - let dt = ref (a, b, c, d, e) in - (match get_refinement () with - | "never" -> () - | "once" -> - List.iter (fun f -> dt := f !dt) (refine_functions ik); - | "fixpoint" -> - let quit_loop = ref false in - while not !quit_loop do - let old_dt = !dt in - List.iter (fun f -> dt := f !dt) (refine_functions ik); - quit_loop := equal old_dt !dt; - if is_bot !dt then dt := bot_of ik; quit_loop := true; - if M.tracing then M.trace "cong-refine-loop" "old: %a, new: %a" pretty old_dt pretty !dt; - done; - | _ -> () - ); !dt - - - (* map with overflow check *) - let mapovc ?(suppress_ovwarn=false) ?(cast=false) ik r (a, b, c, d, e) = - let map f ?no_ov = function Some x -> Some (f ?no_ov x) | _ -> None in - let intv = map (r.f1_ovc (module I2)) b in - let intv_set = map (r.f1_ovc (module I5)) e in - let no_ov = check_ov ~suppress_ovwarn ~cast ik intv intv_set in - let no_ov = no_ov || should_ignore_overflow ik in - refine ik - ( map (fun ?no_ov x -> r.f1_ovc ?no_ov (module I1) x |> fst) a - , BatOption.map fst intv - , map (fun ?no_ov x -> r.f1_ovc ?no_ov (module I3) x |> fst) c - , map (fun ?no_ov x -> r.f1_ovc ?no_ov (module I4) x |> fst) ~no_ov d - , BatOption.map fst intv_set ) - - (* map2 with overflow check *) - let map2ovc ?(cast=false) ik r (xa, xb, xc, xd, xe) (ya, yb, yc, yd, ye) = - let intv = opt_map2 (r.f2_ovc (module I2)) xb yb in - let intv_set = opt_map2 (r.f2_ovc (module I5)) xe ye in - let no_ov = check_ov ~cast ik intv intv_set in - let no_ov = no_ov || should_ignore_overflow ik in - refine ik - ( opt_map2 (fun ?no_ov x y -> r.f2_ovc ?no_ov (module I1) x y |> fst) xa ya - , BatOption.map fst intv - , opt_map2 (fun ?no_ov x y -> r.f2_ovc ?no_ov (module I3) x y |> fst) xc yc - , opt_map2 (fun ?no_ov x y -> r.f2_ovc ?no_ov (module I4) x y |> fst) ~no_ov:no_ov xd yd - , BatOption.map fst intv_set ) - - let map ik r (a, b, c, d, e) = - refine ik - BatOption. - ( map (r.f1 (module I1)) a - , map (r.f1 (module I2)) b - , map (r.f1 (module I3)) c - , map (r.f1 (module I4)) d - , map (r.f1 (module I5)) e) - - let map2 ?(norefine=false) ik r (xa, xb, xc, xd, xe) (ya, yb, yc, yd, ye) = - let r = - ( opt_map2 (r.f2 (module I1)) xa ya - , opt_map2 (r.f2 (module I2)) xb yb - , opt_map2 (r.f2 (module I3)) xc yc - , opt_map2 (r.f2 (module I4)) xd yd - , opt_map2 (r.f2 (module I5)) xe ye) - in - if norefine then r else refine ik r - - - (* f1: unary ops *) - let neg ?no_ov ik = - mapovc ik {f1_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.neg ?no_ov ik)} - - let lognot ik = - map ik {f1 = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.lognot ik)} - - let c_lognot ik = - map ik {f1 = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.c_lognot ik)} - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov t = - mapovc ~suppress_ovwarn ~cast:true t {f1_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.cast_to ?torg ?no_ov t)} - - (* fp: projections *) - let equal_to i x = - let xs = mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.equal_to i } x |> Tuple5.enum |> List.of_enum |> List.filter_map identity in - if List.mem `Eq xs then `Eq else - if List.mem `Neq xs then `Neq else - `Top - - let to_bool = same string_of_bool % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.to_bool } - let minimal = flat (List.max ~cmp:Z.compare) % mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.minimal } - let maximal = flat (List.min ~cmp:Z.compare) % mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.maximal } - (* others *) - let show x = - match to_int x with - | Some v when not (GobConfig.get_bool "dbg.full-output") -> Z.to_string v - | _ -> mapp { fp = fun (type a) (module I:SOverflow with type t = a) x -> I.name () ^ ":" ^ (I.show x) } x - |> to_list - |> String.concat "; " - let to_yojson = [%to_yojson: Yojson.Safe.t list] % to_list % mapp { fp = fun (type a) (module I:SOverflow with type t = a) x -> I.to_yojson x } - - (* `map/opt_map` are used by `project` *) - let opt_map b f = - curry @@ function None, true -> f | x, y when y || b -> x | _ -> None - let map ~keep r (i1, i2, i3, i4, i5) (b1, b2, b3, b4, b5) = - ( opt_map keep (r.f3 (module I1)) i1 b1 - , opt_map keep (r.f3 (module I2)) i2 b2 - , opt_map keep (r.f3 (module I3)) i3 b3 - , opt_map keep (r.f3 (module I4)) i4 b4 - , opt_map keep (r.f3 (module I5)) i5 b5 ) - - (** Project tuple t to precision p - * We have to deactivate IntDomains after the refinement, since we might - * lose information if we do it before. E.g. only "Interval" is active - * and shall be projected to only "Def_Exc". By seting "Interval" to None - * before refinement we have no information for "Def_Exc". - * - * Thus we have 3 Steps: - * 1. Add padding to t by setting `None` to `I.top_of ik` if p is true for this element - * 2. Refine the padded t - * 3. Set elements of t to `None` if p is false for this element - * - * Side Note: - * ~keep is used to reuse `map/opt_map` for Step 1 and 3. - * ~keep:true will keep elements that are `Some x` but should be set to `None` by p. - * This way we won't loose any information for the refinement. - * ~keep:false will set the elements to `None` as defined by p *) - let project ik (p: int_precision) t = - let t_padded = map ~keep:true { f3 = fun (type a) (module I:SOverflow with type t = a) -> Some (I.top_of ik) } t p in - let t_refined = refine ik t_padded in - map ~keep:false { f3 = fun (type a) (module I:SOverflow with type t = a) -> None } t_refined p - - - (* f2: binary ops *) - let join ik = - map2 ~norefine:true ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.join ik)} - - let meet ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.meet ik)} - - let widen ik = - map2 ~norefine:true ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.widen ik)} - - let narrow ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.narrow ik)} - - let add ?no_ov ik = - map2ovc ik - {f2_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.add ?no_ov ik)} - - let sub ?no_ov ik = - map2ovc ik - {f2_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.sub ?no_ov ik)} - - let mul ?no_ov ik = - map2ovc ik - {f2_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.mul ?no_ov ik)} - - let div ?no_ov ik = - map2ovc ik - {f2_ovc = (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.div ?no_ov ik)} - - let rem ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.rem ik)} - - let lt ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.lt ik)} - - let gt ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.gt ik)} - - let le ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.le ik)} - - let ge ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.ge ik)} - - let eq ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.eq ik)} - - let ne ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.ne ik)} - - let logand ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.logand ik)} - - let logor ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.logor ik)} - - let logxor ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.logxor ik)} - - let shift_left ik = - map2ovc ik {f2_ovc= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.shift_left ik)} - - let shift_right ik = - map2ovc ik {f2_ovc= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.shift_right ik)} - - let c_logand ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.c_logand ik)} - - let c_logor ik = - map2 ik {f2= (fun (type a) (module I : SOverflow with type t = a) ?no_ov -> I.c_logor ik)} - - - (* printing boilerplate *) - let pretty_diff () (x,y) = dprintf "%a instead of %a" pretty x pretty y - let printXml f x = - match to_int x with - | Some v when not (GobConfig.get_bool "dbg.full-output") -> BatPrintf.fprintf f "\n\n%s\n\n\n" (Z.to_string v) - | _ -> BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) - - let invariant_ikind e ik ((_, _, _, x_cong, x_intset) as x) = - (* TODO: do refinement before to ensure incl_list being more precise than intervals, etc (https://github.com/goblint/analyzer/pull/1517#discussion_r1693998515), requires refine functions to actually refine that *) - let simplify_int fallback = - match to_int x with - | Some v -> - (* If definite, output single equality instead of every subdomain repeating same equality (or something less precise). *) - IntInvariant.of_int e ik v - | None -> - fallback () - in - let simplify_all () = - match to_incl_list x with - | Some ps -> - (* If inclusion set, output disjunction of equalities because it subsumes interval(s), exclusion set and congruence. *) - IntInvariant.of_incl_list e ik ps - | None -> - (* Get interval bounds from all domains (intervals and exclusion set ranges). *) - let min = minimal x in - let max = maximal x in - let ns = Option.map fst (to_excl_list x) |? [] in (* Ignore exclusion set bit range, known via interval bounds already. *) - (* "Refine" out-of-bounds exclusions for simpler output. *) - let ns = Option.map_default (fun min -> List.filter (Z.leq min) ns) ns min in - let ns = Option.map_default (fun max -> List.filter (Z.geq max) ns) ns max in - Invariant.( - IntInvariant.of_interval_opt e ik (min, max) && (* Output best interval bounds once instead of multiple subdomains repeating them (or less precise ones). *) - IntInvariant.of_excl_list e ik ns && - Option.map_default (I4.invariant_ikind e ik) Invariant.none x_cong && (* Output congruence as is. *) - Option.map_default (I5.invariant_ikind e ik) Invariant.none x_intset (* Output interval sets as is. *) - ) - in - let simplify_none () = - let is = to_list (mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.invariant_ikind e ik } x) in - List.fold_left (fun a i -> - Invariant.(a && i) - ) (Invariant.top ()) is - in - match GobConfig.get_string "ana.base.invariant.int.simplify" with - | "none" -> simplify_none () - | "int" -> simplify_int simplify_none - | "all" -> simplify_int simplify_all - | _ -> assert false - - let arbitrary ik = QCheck.(set_print show @@ tup5 (option (I1.arbitrary ik)) (option (I2.arbitrary ik)) (option (I3.arbitrary ik)) (option (I4.arbitrary ik)) (option (I5.arbitrary ik))) - - let relift (a, b, c, d, e) = - (Option.map I1.relift a, Option.map I2.relift b, Option.map I3.relift c, Option.map I4.relift d, Option.map I5.relift e) -end - -module IntDomTuple = -struct - module I = IntDomLifter (IntDomTupleImpl) - include I - - let top () = failwith "top in IntDomTuple not supported. Use top_of instead." - let no_interval (x: I.t) = {x with v = IntDomTupleImpl.no_interval x.v} - - let no_intervalSet (x: I.t) = {x with v = IntDomTupleImpl.no_intervalSet x.v} -end - -let of_const (i, ik, str) = IntDomTuple.of_int ik i From 8c563ae9c3af6b1d0b4ceb2fdffbc9b1e7e94cd1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 16:54:49 +0300 Subject: [PATCH 461/566] Rename IntDomain0 -> CongruenceDomain for split --- .../value/cdomains/{intDomain0.ml => int/congruenceDomain.ml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/cdomain/value/cdomains/{intDomain0.ml => int/congruenceDomain.ml} (100%) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/int/congruenceDomain.ml similarity index 100% rename from src/cdomain/value/cdomains/intDomain0.ml rename to src/cdomain/value/cdomains/int/congruenceDomain.ml From d66919e5b7ffb07fde6ae1929ca0c8d486f2acba Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 16:55:25 +0300 Subject: [PATCH 462/566] Remove non-CongruenceDomain parts --- .../value/cdomains/int/congruenceDomain.ml | 2776 +---------------- 1 file changed, 1 insertion(+), 2775 deletions(-) diff --git a/src/cdomain/value/cdomains/int/congruenceDomain.ml b/src/cdomain/value/cdomains/int/congruenceDomain.ml index 7450e8a212..a88ffbc813 100644 --- a/src/cdomain/value/cdomains/int/congruenceDomain.ml +++ b/src/cdomain/value/cdomains/int/congruenceDomain.ml @@ -1,2747 +1,5 @@ -open GobConfig -open GoblintCil -open Pretty -open PrecisionUtil - -module M = Messages - -let (%) = Batteries.(%) -let (|?) = Batteries.(|?) - -exception IncompatibleIKinds of string -exception Unknown -exception Error -exception ArithmeticOnIntegerBot of string - - - - -(** Define records that hold mutable variables representing different Configuration values. - * These values are used to keep track of whether or not the corresponding Config values are en-/disabled *) -type ana_int_config_values = { - mutable interval_threshold_widening : bool option; - mutable interval_narrow_by_meet : bool option; - mutable def_exc_widen_by_join : bool option; - mutable interval_threshold_widening_constants : string option; - mutable refinement : string option; -} - -let ana_int_config: ana_int_config_values = { - interval_threshold_widening = None; - interval_narrow_by_meet = None; - def_exc_widen_by_join = None; - interval_threshold_widening_constants = None; - refinement = None; -} - -let get_interval_threshold_widening () = - if ana_int_config.interval_threshold_widening = None then - ana_int_config.interval_threshold_widening <- Some (get_bool "ana.int.interval_threshold_widening"); - Option.get ana_int_config.interval_threshold_widening - -let get_interval_narrow_by_meet () = - if ana_int_config.interval_narrow_by_meet = None then - ana_int_config.interval_narrow_by_meet <- Some (get_bool "ana.int.interval_narrow_by_meet"); - Option.get ana_int_config.interval_narrow_by_meet - -let get_def_exc_widen_by_join () = - if ana_int_config.def_exc_widen_by_join = None then - ana_int_config.def_exc_widen_by_join <- Some (get_bool "ana.int.def_exc_widen_by_join"); - Option.get ana_int_config.def_exc_widen_by_join - -let get_interval_threshold_widening_constants () = - if ana_int_config.interval_threshold_widening_constants = None then - ana_int_config.interval_threshold_widening_constants <- Some (get_string "ana.int.interval_threshold_widening_constants"); - Option.get ana_int_config.interval_threshold_widening_constants - -let get_refinement () = - if ana_int_config.refinement = None then - ana_int_config.refinement <- Some (get_string "ana.int.refinement"); - Option.get ana_int_config.refinement - - - -(** Whether for a given ikind, we should compute with wrap-around arithmetic. - * Always for unsigned types, for signed types if 'sem.int.signed_overflow' is 'assume_wraparound' *) -let should_wrap ik = not (Cil.isSigned ik) || get_string "sem.int.signed_overflow" = "assume_wraparound" - -(** Whether for a given ikind, we should assume there are no overflows. - * Always false for unsigned types, true for signed types if 'sem.int.signed_overflow' is 'assume_none' *) -let should_ignore_overflow ik = Cil.isSigned ik && get_string "sem.int.signed_overflow" = "assume_none" - -let widening_thresholds = ResettableLazy.from_fun WideningThresholds.thresholds -let widening_thresholds_desc = ResettableLazy.from_fun (List.rev % WideningThresholds.thresholds) - -type overflow_info = { overflow: bool; underflow: bool;} - -let set_overflow_flag ~cast ~underflow ~overflow ik = - if !AnalysisState.executing_speculative_computations then - (* Do not produce warnings when the operations are not actually happening in code *) - () - else - let signed = Cil.isSigned ik in - if !AnalysisState.postsolving && signed && not cast then - AnalysisState.svcomp_may_overflow := true; - let sign = if signed then "Signed" else "Unsigned" in - match underflow, overflow with - | true, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190; CWE 191] "%s integer overflow and underflow" sign - | true, false -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 191] "%s integer underflow" sign - | false, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190] "%s integer overflow" sign - | false, false -> assert false - -let reset_lazy () = - ResettableLazy.reset widening_thresholds; - ResettableLazy.reset widening_thresholds_desc; - ana_int_config.interval_threshold_widening <- None; - ana_int_config.interval_narrow_by_meet <- None; - ana_int_config.def_exc_widen_by_join <- None; - ana_int_config.interval_threshold_widening_constants <- None; - ana_int_config.refinement <- None - -module type Arith = -sig - type t - val neg: t -> t - val add: t -> t -> t - val sub: t -> t -> t - val mul: t -> t -> t - val div: t -> t -> t - val rem: t -> t -> t - - val lt: t -> t -> t - val gt: t -> t -> t - val le: t -> t -> t - val ge: t -> t -> t - val eq: t -> t -> t - val ne: t -> t -> t - - val lognot: t -> t - val logand: t -> t -> t - val logor : t -> t -> t - val logxor: t -> t -> t - - val shift_left : t -> t -> t - val shift_right: t -> t -> t - - val c_lognot: t -> t - val c_logand: t -> t -> t - val c_logor : t -> t -> t +open IntDomain0 -end - -module type ArithIkind = -sig - type t - val neg: Cil.ikind -> t -> t - val add: Cil.ikind -> t -> t -> t - val sub: Cil.ikind -> t -> t -> t - val mul: Cil.ikind -> t -> t -> t - val div: Cil.ikind -> t -> t -> t - val rem: Cil.ikind -> t -> t -> t - - val lt: Cil.ikind -> t -> t -> t - val gt: Cil.ikind -> t -> t -> t - val le: Cil.ikind -> t -> t -> t - val ge: Cil.ikind -> t -> t -> t - val eq: Cil.ikind -> t -> t -> t - val ne: Cil.ikind -> t -> t -> t - - val lognot: Cil.ikind -> t -> t - val logand: Cil.ikind -> t -> t -> t - val logor : Cil.ikind -> t -> t -> t - val logxor: Cil.ikind -> t -> t -> t - - val shift_left : Cil.ikind -> t -> t -> t - val shift_right: Cil.ikind -> t -> t -> t - - val c_lognot: Cil.ikind -> t -> t - val c_logand: Cil.ikind -> t -> t -> t - val c_logor : Cil.ikind -> t -> t -> t - -end - -(* Shared functions between S and Z *) -module type B = -sig - include Lattice.S - type int_t - val bot_of: Cil.ikind -> t - val top_of: Cil.ikind -> t - val to_int: t -> int_t option - val equal_to: int_t -> t -> [`Eq | `Neq | `Top] - - val to_bool: t -> bool option - val to_excl_list: t -> (int_t list * (int64 * int64)) option - val of_excl_list: Cil.ikind -> int_t list -> t - val is_excl_list: t -> bool - - val to_incl_list: t -> int_t list option - - val maximal : t -> int_t option - val minimal : t -> int_t option - - val cast_to: ?suppress_ovwarn:bool -> ?torg:Cil.typ -> Cil.ikind -> t -> t -end - -(** Interface of IntDomain implementations that do not take ikinds for arithmetic operations yet. TODO: Should be ported to S in the future. *) -module type IkindUnawareS = -sig - include B - include Arith with type t := t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: int_t -> t - val of_bool: bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val arbitrary: unit -> t QCheck.arbitrary - val invariant: Cil.exp -> t -> Invariant.t -end - -(** Interface of IntDomain implementations taking an ikind for arithmetic operations *) -module type S = -sig - include B - include ArithIkind with type t:= t - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val neg : ?no_ov:bool -> Cil.ikind -> t -> t - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t - - val join: Cil.ikind -> t -> t -> t - val meet: Cil.ikind -> t -> t -> t - val narrow: Cil.ikind -> t -> t -> t - val widen: Cil.ikind -> t -> t -> t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val is_top_of: Cil.ikind -> t -> bool - val invariant_ikind : Cil.exp -> Cil.ikind -> t -> Invariant.t - - val refine_with_congruence: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_interval: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_excl_list: Cil.ikind -> t -> (int_t list * (int64 * int64)) option -> t - val refine_with_incl_list: Cil.ikind -> t -> int_t list option -> t - - val project: Cil.ikind -> int_precision -> t -> t - val arbitrary: Cil.ikind -> t QCheck.arbitrary -end - -module type SOverflow = -sig - - include S - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val neg : ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val of_int : Cil.ikind -> int_t -> t * overflow_info - - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t * overflow_info - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - - val shift_left : Cil.ikind -> t -> t -> t * overflow_info - - val shift_right : Cil.ikind -> t -> t -> t * overflow_info -end - -module type Y = -sig - (* include B *) - include B - include Arith with type t:= t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val is_top_of: Cil.ikind -> t -> bool - - val project: int_precision -> t -> t - val invariant: Cil.exp -> t -> Invariant.t -end - -module type Z = Y with type int_t = Z.t - - -module IntDomLifter (I : S) = -struct - open Cil - 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 (GobPretty.sprintf "ikinds %a and %a are incompatible. Values: %a and %a" CilType.Ikind.pretty x.ikind CilType.Ikind.pretty y.ikind I.pretty x.v I.pretty y.v)) - let lift op x = {x with v = op x.ikind x.v } - (* For logical operations the result is of type int *) - let lift_logical op x = {v = op x.ikind x.v; ikind = Cil.IInt} - let lift2 op x y = check_ikinds x y; {x with v = op x.ikind x.v y.v } - let lift2_cmp op x y = check_ikinds x y; {v = op x.ikind x.v y.v; ikind = Cil.IInt} - - let bot_of ikind = { v = I.bot_of ikind; ikind} - let bot () = failwith "bot () is not implemented for IntDomLifter." - let is_bot x = I.is_bot x.v - let top_of ikind = { v = I.top_of ikind; ikind} - let top () = failwith "top () is not implemented for IntDomLifter." - let is_top x = I.is_top x.v - - (* Leq does not check for ikind, because it is used in invariant with arguments of different type. - TODO: check ikinds here and fix invariant to work with right ikinds *) - let leq x y = I.leq x.v y.v - let join = lift2 I.join - let meet = lift2 I.meet - let widen = lift2 I.widen - let narrow = lift2 I.narrow - - let show x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - "⊤" - else - I.show x.v (* TODO add ikind to output *) - let pretty () x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - Pretty.text "⊤" - else - I.pretty () x.v (* TODO add ikind to output *) - let pretty_diff () (x, y) = I.pretty_diff () (x.v, y.v) (* TODO check ikinds, add them to output *) - let printXml o x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - BatPrintf.fprintf o "\n\n⊤\n\n\n" - else - I.printXml o x.v (* TODO add ikind to output *) - (* This is for debugging *) - let name () = "IntDomLifter(" ^ (I.name ()) ^ ")" - let to_yojson x = I.to_yojson x.v - let invariant e x = - let e' = Cilfacade.mkCast ~e ~newt:(TInt (x.ikind, [])) in - I.invariant_ikind e' x.ikind x.v - let tag x = I.tag x.v - let arbitrary ik = failwith @@ "Arbitrary not implement for " ^ (name ()) ^ "." - let to_int x = I.to_int x.v - let of_int ikind x = { v = I.of_int ikind x; ikind} - let equal_to i x = I.equal_to i x.v - let to_bool x = I.to_bool x.v - let of_bool ikind b = { v = I.of_bool ikind b; ikind} - let to_excl_list x = I.to_excl_list x.v - let of_excl_list ikind is = {v = I.of_excl_list ikind is; ikind} - let is_excl_list x = I.is_excl_list x.v - let to_incl_list x = I.to_incl_list x.v - let of_interval ?(suppress_ovwarn=false) ikind (lb,ub) = {v = I.of_interval ~suppress_ovwarn ikind (lb,ub); ikind} - let of_congruence ikind (c,m) = {v = I.of_congruence ikind (c,m); ikind} - let starting ?(suppress_ovwarn=false) ikind i = {v = I.starting ~suppress_ovwarn ikind i; ikind} - let ending ?(suppress_ovwarn=false) ikind i = {v = I.ending ~suppress_ovwarn ikind i; ikind} - let maximal x = I.maximal x.v - let minimal x = I.minimal x.v - - let neg = lift I.neg - let add = lift2 I.add - let sub = lift2 I.sub - let mul = lift2 I.mul - let div = lift2 I.div - let rem = lift2 I.rem - let lt = lift2_cmp I.lt - let gt = lift2_cmp I.gt - let le = lift2_cmp I.le - let ge = lift2_cmp I.ge - let eq = lift2_cmp I.eq - let ne = lift2_cmp I.ne - let lognot = lift I.lognot - let logand = lift2 I.logand - let logor = lift2 I.logor - let logxor = lift2 I.logxor - let shift_left x y = {x with v = I.shift_left x.ikind x.v y.v } (* TODO check ikinds*) - let shift_right x y = {x with v = I.shift_right x.ikind x.v y.v } (* TODO check ikinds*) - let c_lognot = lift_logical I.c_lognot - let c_logand = lift2 I.c_logand - let c_logor = lift2 I.c_logor - - let cast_to ?(suppress_ovwarn=false) ?torg ikind x = {v = I.cast_to ~suppress_ovwarn ~torg:(TInt(x.ikind,[])) ikind x.v; ikind} - - let is_top_of ik x = ik = x.ikind && I.is_top_of ik x.v - - let relift x = { v = I.relift x.v; ikind = x.ikind } - - let project p v = { v = I.project v.ikind p v.v; ikind = v.ikind } -end - -module type Ikind = -sig - val ikind: unit -> Cil.ikind -end - -module PtrDiffIkind : Ikind = -struct - let ikind = Cilfacade.ptrdiff_ikind -end - -module IntDomWithDefaultIkind (I: Y) (Ik: Ikind) : Y with type t = I.t and type int_t = I.int_t = -struct - include I - let top () = I.top_of (Ik.ikind ()) - let bot () = I.bot_of (Ik.ikind ()) -end - -module Size = struct (* size in bits as int, range as int64 *) - open Cil - let sign x = if Z.compare x Z.zero < 0 then `Signed else `Unsigned - - let top_typ = TInt (ILongLong, []) - let min_for x = intKindForValue x (sign x = `Unsigned) - let bit = function (* bits needed for representation *) - | IBool -> 1 - | ik -> bytesSizeOfInt ik * 8 - let is_int64_big_int x = Z.fits_int64 x - let card ik = (* cardinality *) - let b = bit ik in - Z.shift_left Z.one b - let bits ik = (* highest bits for neg/pos values *) - let s = bit ik in - if isSigned ik then s-1, s-1 else 0, s - let bits_i64 ik = BatTuple.Tuple2.mapn Int64.of_int (bits ik) - let range ik = - let a,b = bits ik in - let x = if isSigned ik then Z.neg (Z.shift_left Z.one a) (* -2^a *) else Z.zero in - let y = Z.pred (Z.shift_left Z.one b) in (* 2^b - 1 *) - x,y - - let is_cast_injective ~from_type ~to_type = - let (from_min, from_max) = range (Cilfacade.get_ikind from_type) in - let (to_min, to_max) = range (Cilfacade.get_ikind to_type) in - if M.tracing then M.trace "int" "is_cast_injective %a (%a, %a) -> %a (%a, %a)" CilType.Typ.pretty from_type GobZ.pretty from_min GobZ.pretty from_max CilType.Typ.pretty to_type GobZ.pretty to_min GobZ.pretty to_max; - Z.compare to_min from_min <= 0 && Z.compare from_max to_max <= 0 - - let cast t x = (* TODO: overflow is implementation-dependent! *) - if t = IBool then - (* C11 6.3.1.2 Boolean type *) - if Z.equal x Z.zero then Z.zero else Z.one - else - let a,b = range t in - let c = card t in - let y = Z.erem x c in - let y = if Z.gt y b then Z.sub y c - else if Z.lt y a then Z.add y c - else y - in - if M.tracing then M.tracel "cast" "Cast %a to range [%a, %a] (%a) = %a (%s in int64)" GobZ.pretty x GobZ.pretty a GobZ.pretty b GobZ.pretty c GobZ.pretty y (if is_int64_big_int y then "fits" else "does not fit"); - y - - let min_range_sign_agnostic x = - let size ik = - let a,b = bits_i64 ik in - Int64.neg a,b - in - if sign x = `Signed then - size (min_for x) - else - let a, b = size (min_for x) in - if b <= 64L then - let upper_bound_less = Int64.sub b 1L in - let max_one_less = Z.(pred @@ shift_left Z.one (Int64.to_int upper_bound_less)) in - if x <= max_one_less then - a, upper_bound_less - else - a,b - else - a, b - - (* From the number of bits used to represent a positive value, determines the maximal representable value *) - let max_from_bit_range pos_bits = Z.(pred @@ shift_left Z.one (to_int (Z.of_int64 pos_bits))) - - (* From the number of bits used to represent a non-positive value, determines the minimal representable value *) - let min_from_bit_range neg_bits = Z.(if neg_bits = 0L then Z.zero else neg @@ shift_left Z.one (to_int (neg (Z.of_int64 neg_bits)))) - -end - - -module StdTop (B: sig type t val top_of: Cil.ikind -> t end) = struct - open B - (* these should be overwritten for better precision if possible: *) - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ik x = top_of ik - let ending ?(suppress_ovwarn=false) ik x = top_of ik - let maximal x = None - let minimal x = None -end - -module Std (B: sig - type t - val name: unit -> string - val top_of: Cil.ikind -> t - val bot_of: Cil.ikind -> t - val show: t -> string - val equal: t -> t -> bool - end) = struct - include Printable.StdLeaf - let name = B.name (* overwrite the one from Printable.Std *) - open B - let is_top x = failwith "is_top not implemented for IntDomain.Std" - let is_bot x = B.equal x (bot_of Cil.IInt) (* Here we assume that the representation of bottom is independent of the ikind - This may be true for intdomain implementations, but not e.g. for IntDomLifter. *) - let is_top_of ik x = B.equal x (top_of ik) - - (* all output is based on B.show *) - include Printable.SimpleShow ( - struct - type nonrec t = t - let show = show - end - ) - let pretty_diff () (x,y) = dprintf "%s: %a instead of %a" (name ()) pretty x pretty y - - include StdTop (B) -end - -(* Textbook interval arithmetic, without any overflow handling etc. *) -module IntervalArith (Ints_t : IntOps.IntOps) = struct - let min4 a b c d = Ints_t.min (Ints_t.min a b) (Ints_t.min c d) - let max4 a b c d = Ints_t.max (Ints_t.max a b) (Ints_t.max c d) - - let mul (x1, x2) (y1, y2) = - let x1y1 = (Ints_t.mul x1 y1) in - let x1y2 = (Ints_t.mul x1 y2) in - let x2y1 = (Ints_t.mul x2 y1) in - let x2y2 = (Ints_t.mul x2 y2) in - (min4 x1y1 x1y2 x2y1 x2y2, max4 x1y1 x1y2 x2y1 x2y2) - - let shift_left (x1,x2) (y1,y2) = - let y1p = Ints_t.shift_left Ints_t.one y1 in - let y2p = Ints_t.shift_left Ints_t.one y2 in - mul (x1, x2) (y1p, y2p) - - let div (x1, x2) (y1, y2) = - let x1y1n = (Ints_t.div x1 y1) in - let x1y2n = (Ints_t.div x1 y2) in - let x2y1n = (Ints_t.div x2 y1) in - let x2y2n = (Ints_t.div x2 y2) in - let x1y1p = (Ints_t.div x1 y1) in - let x1y2p = (Ints_t.div x1 y2) in - let x2y1p = (Ints_t.div x2 y1) in - let x2y2p = (Ints_t.div x2 y2) in - (min4 x1y1n x1y2n x2y1n x2y2n, max4 x1y1p x1y2p x2y1p x2y2p) - - let add (x1, x2) (y1, y2) = (Ints_t.add x1 y1, Ints_t.add x2 y2) - let sub (x1, x2) (y1, y2) = (Ints_t.sub x1 y2, Ints_t.sub x2 y1) - - let neg (x1, x2) = (Ints_t.neg x2, Ints_t.neg x1) - - let one = (Ints_t.one, Ints_t.one) - let zero = (Ints_t.zero, Ints_t.zero) - let top_bool = (Ints_t.zero, Ints_t.one) - - let to_int (x1, x2) = - if Ints_t.equal x1 x2 then Some x1 else None - - let upper_threshold u max_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - let max_ik' = Ints_t.to_bigint max_ik in - let t = List.find_opt (fun x -> Z.compare u x <= 0 && Z.compare x max_ik' <= 0) ts in - BatOption.map_default Ints_t.of_bigint max_ik t - let lower_threshold l min_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - let min_ik' = Ints_t.to_bigint min_ik in - let t = List.find_opt (fun x -> Z.compare l x >= 0 && Z.compare x min_ik' >= 0) ts in - BatOption.map_default Ints_t.of_bigint min_ik t - let is_upper_threshold u = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - List.exists (Z.equal u) ts - let is_lower_threshold l = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - List.exists (Z.equal l) ts -end - -module IntInvariant = -struct - let of_int e ik x = - if get_bool "witness.invariant.exact" then - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) - else - Invariant.none - - let of_incl_list e ik ps = - match ps with - | [_; _] when ik = IBool && not (get_bool "witness.invariant.inexact-type-bounds") -> - assert (List.mem Z.zero ps); - assert (List.mem Z.one ps); - Invariant.none - | [_] when get_bool "witness.invariant.exact" -> - Invariant.none - | _ :: _ :: _ - | [_] | [] -> - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in - Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) (Invariant.bot ()) ps - - let of_interval_opt e ik = function - | (Some x1, Some x2) when Z.equal x1 x2 -> - of_int e ik x1 - | x1_opt, x2_opt -> - let (min_ik, max_ik) = Size.range ik in - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in - let i1 = - match x1_opt, inexact_type_bounds with - | Some x1, false when Z.equal min_ik x1 -> Invariant.none - | Some x1, _ -> Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1, e, intType)) - | None, _ -> Invariant.none - in - let i2 = - match x2_opt, inexact_type_bounds with - | Some x2, false when Z.equal x2 max_ik -> Invariant.none - | Some x2, _ -> Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) - | None, _ -> Invariant.none - in - Invariant.(i1 && i2) - - let of_interval e ik (x1, x2) = - of_interval_opt e ik (Some x1, Some x2) - - let of_excl_list e ik ns = - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in - Invariant.(a && i) - ) (Invariant.top ()) ns -end - -module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = -struct - let name () = "intervals" - type int_t = Ints_t.t - type t = (Ints_t.t * Ints_t.t) option [@@deriving eq, ord, hash] - module IArith = IntervalArith (Ints_t) - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - let top_of ik = Some (range ik) - let bot () = None - let bot_of ik = bot () (* TODO: improve *) - - let show = function None -> "bottom" | Some (x,y) -> "["^Ints_t.to_string x^","^Ints_t.to_string y^"]" - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let equal_to i = function - | None -> failwith "unsupported: equal_to with bottom" - | Some (a, b) -> - if a = b && b = i then `Eq else if Ints_t.compare a i <= 0 && Ints_t.compare i b <=0 then `Top else `Neq - - let norm ?(suppress_ovwarn=false) ?(cast=false) ik : (t -> t * overflow_info) = function None -> (None, {underflow=false; overflow=false}) | Some (x,y) -> - if Ints_t.compare x y > 0 then - (None,{underflow=false; overflow=false}) - else ( - let (min_ik, max_ik) = range ik in - let underflow = Ints_t.compare min_ik x > 0 in - let overflow = Ints_t.compare max_ik y < 0 in - let ov_info = { underflow = underflow && not suppress_ovwarn; overflow = overflow && not suppress_ovwarn } in - let v = - if underflow || overflow then - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (Ints_t.sub max_ik min_ik) in - let resdiff = Ints_t.abs (Ints_t.sub y x) in - if Ints_t.compare resdiff diff > 0 then - top_of ik - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if Ints_t.compare l u <= 0 then - Some (l, u) - else - (* Interval that wraps around (begins to the right of its end). We can not represent such intervals *) - top_of ik - else if not cast && should_ignore_overflow ik then - let tl, tu = BatOption.get @@ top_of ik in - Some (Ints_t.max tl x, Ints_t.min tu y) - else - top_of ik - else - Some (x,y) - in - (v, ov_info) - ) - - let leq (x:t) (y:t) = - match x, y with - | None, _ -> true - | Some _, None -> false - | Some (x1,x2), Some (y1,y2) -> Ints_t.compare x1 y1 >= 0 && Ints_t.compare x2 y2 <= 0 - - let join ik (x:t) y = - match x, y with - | None, z | z, None -> z - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.min x1 y1, Ints_t.max x2 y2) |> fst - - let meet ik (x:t) y = - match x, y with - | None, z | z, None -> None - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.max x1 y1, Ints_t.min x2 y2) |> fst - - (* TODO: change to_int signature so it returns a big_int *) - let to_int x = Option.bind x (IArith.to_int) - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm ~suppress_ovwarn ik @@ Some (x,y) - let of_int ik (x: int_t) = of_interval ik (x,x) - let zero = Some IArith.zero - let one = Some IArith.one - let top_bool = Some IArith.top_bool - - let of_bool _ik = function true -> one | false -> zero - let to_bool (a: t) = match a with - | None -> None - | Some (l, u) when Ints_t.compare l Ints_t.zero = 0 && Ints_t.compare u Ints_t.zero = 0 -> Some false - | x -> if leq zero x then None else Some true - - let starting ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (fst (range ik), n) - - (* TODO: change signature of maximal, minimal to return big_int*) - let maximal = function None -> None | Some (x,y) -> Some y - let minimal = function None -> None | Some (x,y) -> Some x - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov t = norm ~cast:true t (* norm does all overflow handling *) - - let widen ik x y = - match x, y with - | None, z | z, None -> z - | Some (l0,u0), Some (l1,u1) -> - let (min_ik, max_ik) = range ik in - let threshold = get_interval_threshold_widening () in - let l2 = - if Ints_t.compare l0 l1 = 0 then l0 - else if threshold then IArith.lower_threshold l1 min_ik - else min_ik - in - let u2 = - if Ints_t.compare u0 u1 = 0 then u0 - else if threshold then IArith.upper_threshold u1 max_ik - else max_ik - in - norm ik @@ Some (l2,u2) |> fst - let widen ik x y = - let r = widen ik x y in - if M.tracing && not (equal x y) then M.tracel "int" "interval widen %a %a -> %a" pretty x pretty y pretty r; - assert (leq x y); (* TODO: remove for performance reasons? *) - r - - let narrow ik x y = - match x, y with - | _,None | None, _ -> None - | Some (x1,x2), Some (y1,y2) -> - let threshold = get_interval_threshold_widening () in - let (min_ik, max_ik) = range ik in - let lr = if Ints_t.compare min_ik x1 = 0 || threshold && Ints_t.compare y1 x1 > 0 && IArith.is_lower_threshold x1 then y1 else x1 in - let ur = if Ints_t.compare max_ik x2 = 0 || threshold && Ints_t.compare y2 x2 < 0 && IArith.is_upper_threshold x2 then y2 else x2 in - norm ik @@ Some (lr,ur) |> fst - - - let narrow ik x y = - if get_interval_narrow_by_meet () then - meet ik x y - else - narrow ik x y - - let log f ~annihilator ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_bool i1, to_bool i2 with - | Some x, _ when x = annihilator -> of_bool ik annihilator - | _, Some y when y = annihilator -> of_bool ik annihilator - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - let c_logor = log (||) ~annihilator:true - let c_logand = log (&&) ~annihilator:false - - let log1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_bool i1 with - | Some x -> of_bool ik (f ik x) - | _ -> top_of ik - - let c_lognot = log1 (fun _ik -> not) - - let bit f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - let bitcomp f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{underflow=false; overflow=false})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let logxor = bit (fun _ik -> Ints_t.logxor) - - let logand ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (Ints_t.logand x y) |> fst with Division_by_zero -> top_of ik) - | _, Some y when Ints_t.equal y Ints_t.zero -> of_int ik Ints_t.zero |> fst - | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst - | _ -> top_of ik - - let logor = bit (fun _ik -> Ints_t.logor) - - let bit1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_int i1 with - | Some x -> of_int ik (f ik x) |> fst - | _ -> top_of ik - - let lognot = bit1 (fun _ik -> Ints_t.lognot) - let shift_right = bitcomp (fun _ik x y -> Ints_t.shift_right x (Ints_t.to_int y)) - - let neg ?no_ov ik = function None -> (None,{underflow=false; overflow=false}) | Some x -> norm ik @@ Some (IArith.neg x) - - let binary_op_with_norm ?no_ov op ik x y = match x, y with - | None, None -> (None, {overflow=false; underflow= false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some x, Some y -> norm ik @@ Some (op x y) - - let add ?no_ov = binary_op_with_norm IArith.add - let mul ?no_ov = binary_op_with_norm IArith.mul - let sub ?no_ov = binary_op_with_norm IArith.sub - - let shift_left ik a b = - match is_bot a, is_bot b with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show a) (show b))) - | _ -> - match a, minimal b, maximal b with - | Some a, Some bl, Some bu when (Ints_t.compare bl Ints_t.zero >= 0) -> - (try - let r = IArith.shift_left a (Ints_t.to_int bl, Ints_t.to_int bu) in - norm ik @@ Some r - with Z.Overflow -> (top_of ik,{underflow=false; overflow=true})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let rem ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (xl, xu), Some (yl, yu) -> - if is_top_of ik x && is_top_of ik y then - (* This is needed to preserve soundness also on things bigger than int32 e.g. *) - (* x: 3803957176L -> T in Interval32 *) - (* y: 4209861404L -> T in Interval32 *) - (* x % y: 3803957176L -> T in Interval32 *) - (* T in Interval32 is [-2147483648,2147483647] *) - (* the code below computes [-2147483647,2147483647] for this though which is unsound *) - top_of ik - else - (* If we have definite values, Ints_t.rem will give a definite result. - * Otherwise we meet with a [range] the result can be in. - * This range is [0, min xu b] if x is positive, and [max xl -b, min xu b] if x can be negative. - * The precise bound b is one smaller than the maximum bound. Negative y give the same result as positive. *) - let pos x = if Ints_t.compare x Ints_t.zero < 0 then Ints_t.neg x else x in - let b = Ints_t.sub (Ints_t.max (pos yl) (pos yu)) Ints_t.one in - let range = if Ints_t.compare xl Ints_t.zero>= 0 then Some (Ints_t.zero, Ints_t.min xu b) else Some (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit (fun _ik -> Ints_t.rem) ik x y) range - - let rec div ?no_ov ik x y = - match x, y with - | None, None -> (bot (),{underflow=false; overflow=false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | (Some (x1,x2) as x), (Some (y1,y2) as y) -> - begin - let is_zero v = Ints_t.compare v Ints_t.zero = 0 in - match y1, y2 with - | l, u when is_zero l && is_zero u -> (top_of ik,{underflow=false; overflow=false}) (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> div ik (Some (x1,x2)) (Some (Ints_t.one,y2)) - | _, u when is_zero u -> div ik (Some (x1,x2)) (Some (y1, Ints_t.(neg one))) - | _ when leq (of_int ik (Ints_t.zero) |> fst) (Some (y1,y2)) -> (top_of ik,{underflow=false; overflow=false}) - | _ -> binary_op_with_norm IArith.div ik x y - end - - let ne ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik true - else if Ints_t.compare x2 y1 <= 0 && Ints_t.compare y2 x1 <= 0 then - of_bool ik false - else top_bool - - let eq ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 && Ints_t.compare x2 y1 <= 0 then - of_bool ik true - else if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik false - else top_bool - - let ge ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 then of_bool ik true - else if Ints_t.compare x2 y1 < 0 then of_bool ik false - else top_bool - - let le ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 <= 0 then of_bool ik true - else if Ints_t.compare y2 x1 < 0 then of_bool ik false - else top_bool - - let gt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 then of_bool ik true - else if Ints_t.compare x2 y1 <= 0 then of_bool ik false - else top_bool - - let lt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 < 0 then of_bool ik true - else if Ints_t.compare y2 x1 <= 0 then of_bool ik false - else top_bool - - let invariant_ikind e ik = function - | Some (x1, x2) -> - let (x1', x2') = BatTuple.Tuple2.mapn Ints_t.to_bigint (x1, x2) in - IntInvariant.of_interval e ik (x1', x2') - | None -> Invariant.none - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let shrink = function - | Some (l, u) -> (return None) <+> (GobQCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) - | None -> empty - in - QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) (fun x -> of_interval ik x |> fst ) pair_arb) - - let modulo n k = - let result = Ints_t.rem n k in - if Ints_t.compare result Ints_t.zero >= 0 then result - else Ints_t.add result k - - let refine_with_congruence ik (intv : t) (cong : (int_t * int_t ) option) : t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if Ints_t.equal m Ints_t.zero && (Ints_t.compare c x < 0 || Ints_t.compare c y > 0) then None - else if Ints_t.equal m Ints_t.zero then - Some (c, c) - else - let (min_ik, max_ik) = range ik in - let rcx = - if Ints_t.equal x min_ik then x else - Ints_t.add x (modulo (Ints_t.sub c x) (Ints_t.abs m)) in - let lcy = - if Ints_t.equal y max_ik then y else - Ints_t.sub y (modulo (Ints_t.sub y c) (Ints_t.abs m)) in - if Ints_t.compare rcx lcy > 0 then None - else if Ints_t.equal rcx lcy then norm ik @@ Some (rcx, rcx) |> fst - else norm ik @@ Some (rcx, lcy) |> fst - | _ -> None - - let refine_with_congruence ik x y = - let refn = refine_with_congruence ik x y in - if M.tracing then M.trace "refine" "int_refine_with_congruence %a %a -> %a" pretty x pretty y pretty refn; - refn - - let refine_with_interval ik a b = meet ik a b - - let refine_with_excl_list ik (intv : t) (excl : (int_t list * (int64 * int64)) option) : t = - match intv, excl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls, (rl, rh)) -> - let rec shrink op b = - let new_b = (op b (Ints_t.of_int(Bool.to_int(BatList.mem_cmp Ints_t.compare b ls)))) in - if not (Ints_t.equal b new_b) then shrink op new_b else new_b - in - let (min_ik, max_ik) = range ik in - let l' = if Ints_t.equal l min_ik then l else shrink Ints_t.add l in - let u' = if Ints_t.equal u max_ik then u else shrink Ints_t.sub u in - let intv' = norm ik @@ Some (l', u') |> fst in - let range = norm ~suppress_ovwarn:true ik (Some (Ints_t.of_bigint (Size.min_from_bit_range rl), Ints_t.of_bigint (Size.max_from_bit_range rh))) |> fst in - meet ik intv' range - - let refine_with_incl_list ik (intv: t) (incl : (int_t list) option) : t = - match intv, incl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls) -> - let rec min m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> min (Some x) xs | Some m -> if Ints_t.compare m x < 0 then min (Some m) xs else min (Some x) xs in - let rec max m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> max (Some x) xs | Some m -> if Ints_t.compare m x > 0 then max (Some m) xs else max (Some x) xs in - match min None ls, max None ls with - | Some m1, Some m2 -> refine_with_interval ik (Some(l, u)) (Some (m1, m2)) - | _, _-> intv - - let project ik p t = t -end - -(** IntervalSetFunctor that is not just disjunctive completion of intervals, but attempts to be precise for wraparound arithmetic for unsigned types *) -module IntervalSetFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) list = -struct - - module Interval = IntervalFunctor (Ints_t) - module IArith = IntervalArith (Ints_t) - - - let name () = "interval_sets" - - type int_t = Ints_t.t - - let (>.) a b = Ints_t.compare a b > 0 - let (=.) a b = Ints_t.compare a b = 0 - let (<.) a b = Ints_t.compare a b < 0 - let (>=.) a b = Ints_t.compare a b >= 0 - let (<=.) a b = Ints_t.compare a b <= 0 - let (+.) a b = Ints_t.add a b - let (-.) a b = Ints_t.sub a b - - (* - Each domain's element is guaranteed to be in canonical form. That is, each interval contained - inside the set does not overlap with each other and they are not adjacent. - *) - type t = (Ints_t.t * Ints_t.t) list [@@deriving eq, hash, ord] - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - - let top_of ik = [range ik] - - let bot () = [] - - let bot_of ik = bot () - - let show (x: t) = - let show_interval i = Printf.sprintf "[%s, %s]" (Ints_t.to_string (fst i)) (Ints_t.to_string (snd i)) in - List.fold_left (fun acc i -> (show_interval i) :: acc) [] x |> List.rev |> String.concat ", " |> Printf.sprintf "[%s]" - - (* New type definition for the sweeping line algorithm used for implementing join/meet functions. *) - type event = Enter of Ints_t.t | Exit of Ints_t.t - - let unbox_event = function Enter x -> x | Exit x -> x - - let cmp_events x y = - (* Deliberately comparing ints first => Cannot be derived *) - let res = Ints_t.compare (unbox_event x) (unbox_event y) in - if res <> 0 then res - else - begin - match (x, y) with - | (Enter _, Exit _) -> -1 - | (Exit _, Enter _) -> 1 - | (_, _) -> 0 - end - - let interval_set_to_events (xs: t) = - List.concat_map (fun (a, b) -> [Enter a; Exit b]) xs - - let two_interval_sets_to_events (xs: t) (ys: t) = - let xs = interval_set_to_events xs in - let ys = interval_set_to_events ys in - List.merge cmp_events xs ys - - (* Using the sweeping line algorithm, combined_event_list returns a new event list representing the intervals in which at least n intervals in xs overlap - This function is used for both join and meet operations with different parameter n: 1 for join, 2 for meet *) - let combined_event_list lattice_op (xs:event list) = - let l = match lattice_op with `Join -> 1 | `Meet -> 2 in - let aux (interval_count, acc) = function - | Enter x -> (interval_count + 1, if (interval_count + 1) >= l && interval_count < l then (Enter x)::acc else acc) - | Exit x -> (interval_count - 1, if interval_count >= l && (interval_count - 1) < l then (Exit x)::acc else acc) - in - List.fold_left aux (0, []) xs |> snd |> List.rev - - let rec events_to_intervals = function - | [] -> [] - | (Enter x)::(Exit y)::xs -> (x, y)::(events_to_intervals xs) - | _ -> failwith "Invalid events list" - - let remove_empty_gaps (xs: t) = - let aux acc (l, r) = match acc with - | ((a, b)::acc') when (b +. Ints_t.one) >=. l -> (a, r)::acc' - | _ -> (l, r)::acc - in - List.fold_left aux [] xs |> List.rev - - let canonize (xs: t) = - interval_set_to_events xs |> - List.sort cmp_events |> - combined_event_list `Join |> - events_to_intervals |> - remove_empty_gaps - - let unop (x: t) op = match x with - | [] -> [] - | _ -> canonize @@ List.concat_map op x - - let binop (x: t) (y: t) op : t = match x, y with - | [], _ -> [] - | _, [] -> [] - | _, _ -> canonize @@ List.concat_map op (BatList.cartesian_product x y) - - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let minimal = function - | [] -> None - | (x, _)::_ -> Some x - - let maximal = function - | [] -> None - | xs -> Some (BatList.last xs |> snd) - - let equal_to_interval i (a, b) = - if a =. b && b =. i then - `Eq - else if a <=. i && i <=. b then - `Top - else - `Neq - - let equal_to i xs = match List.map (equal_to_interval i) xs with - | [] -> failwith "unsupported: equal_to with bottom" - | [`Eq] -> `Eq - | ys when List.for_all ((=) `Neq) ys -> `Neq - | _ -> `Top - - let norm_interval ?(suppress_ovwarn=false) ?(cast=false) ik (x,y) : t*overflow_info = - if x >. y then - ([],{underflow=false; overflow=false}) - else - let (min_ik, max_ik) = range ik in - let underflow = min_ik >. x in - let overflow = max_ik <. y in - let v = if underflow || overflow then - begin - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (max_ik -. min_ik) in - let resdiff = Ints_t.abs (y -. x) in - if resdiff >. diff then - [range ik] - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if l <=. u then - [(l, u)] - else - (* Interval that wraps around (begins to the right of its end). We CAN represent such intervals *) - [(min_ik, u); (l, max_ik)] - else if not cast && should_ignore_overflow ik then - [Ints_t.max min_ik x, Ints_t.min max_ik y] - else - [range ik] - end - else - [(x,y)] - in - if suppress_ovwarn then (v, {underflow=false; overflow=false}) else (v, {underflow; overflow}) - - let norm_intvs ?(suppress_ovwarn=false) ?(cast=false) (ik:ikind) (xs: t) : t*overflow_info = - let res = List.map (norm_interval ~suppress_ovwarn ~cast ik) xs in - let intvs = List.concat_map fst res in - let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in - let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in - (canonize intvs,{underflow; overflow}) - - let binary_op_with_norm op (ik:ikind) (x: t) (y: t) : t*overflow_info = match x, y with - | [], _ -> ([],{overflow=false; underflow=false}) - | _, [] -> ([],{overflow=false; underflow=false}) - | _, _ -> norm_intvs ik @@ List.concat_map (fun (x,y) -> [op x y]) (BatList.cartesian_product x y) - - let binary_op_with_ovc (x: t) (y: t) op : t*overflow_info = match x, y with - | [], _ -> ([],{overflow=false; underflow=false}) - | _, [] -> ([],{overflow=false; underflow=false}) - | _, _ -> - let res = List.map op (BatList.cartesian_product x y) in - let intvs = List.concat_map fst res in - let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in - let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in - (canonize intvs,{underflow; overflow}) - - let unary_op_with_norm op (ik:ikind) (x: t) = match x with - | [] -> ([],{overflow=false; underflow=false}) - | _ -> norm_intvs ik @@ List.concat_map (fun x -> [op x]) x - - let rec leq (xs: t) (ys: t) = - let leq_interval (al, au) (bl, bu) = al >=. bl && au <=. bu in - match xs, ys with - | [], _ -> true - | _, [] -> false - | (xl,xr)::xs', (yl,yr)::ys' -> - if leq_interval (xl,xr) (yl,yr) then - leq xs' ys - else if xr <. yl then - false - else - leq xs ys' - - let join ik (x: t) (y: t): t = - two_interval_sets_to_events x y |> - combined_event_list `Join |> - events_to_intervals |> - remove_empty_gaps - - let meet ik (x: t) (y: t): t = - two_interval_sets_to_events x y |> - combined_event_list `Meet |> - events_to_intervals - - let to_int = function - | [x] -> IArith.to_int x - | _ -> None - - let zero = [IArith.zero] - let one = [IArith.one] - let top_bool = [IArith.top_bool] - - let not_bool (x:t) = - let is_false x = equal x zero in - let is_true x = equal x one in - if is_true x then zero else if is_false x then one else top_bool - - let to_bool = function - | [(l,u)] when l =. Ints_t.zero && u =. Ints_t.zero -> Some false - | x -> if leq zero x then None else Some true - - let of_bool _ = function true -> one | false -> zero - - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm_interval ~suppress_ovwarn ~cast:false ik (x,y) - - let of_int ik (x: int_t) = of_interval ik (x, x) - - let lt ik x y = - match x, y with - | [], [] -> bot_of ik - | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> - let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in - let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in - if max_x <. min_y then - of_bool ik true - else if min_x >=. max_y then - of_bool ik false - else - top_bool - - let le ik x y = - match x, y with - | [], [] -> bot_of ik - | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> - let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in - let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in - if max_x <=. min_y then - of_bool ik true - else if min_x >. max_y then - of_bool ik false - else - top_bool - - let gt ik x y = not_bool @@ le ik x y - - let ge ik x y = not_bool @@ lt ik x y - - let eq ik x y = match x, y with - | (a, b)::[], (c, d)::[] when a =. b && c =. d && a =. c -> - one - | _ -> - if is_bot (meet ik x y) then - zero - else - top_bool - - let ne ik x y = not_bool @@ eq ik x y - let interval_to_int i = Interval.to_int (Some i) - let interval_to_bool i = Interval.to_bool (Some i) - - let log f ik (i1, i2) = - match (interval_to_bool i1, interval_to_bool i2) with - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - - let bit f ik (i1, i2) = - match (interval_to_int i1), (interval_to_int i2) with - | Some x, Some y -> (try of_int ik (f x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - - let bitcomp f ik (i1, i2) = - match (interval_to_int i1, interval_to_int i2) with - | Some x, Some y -> (try of_int ik (f x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{overflow=false; underflow=false})) - | _, _ -> (top_of ik,{overflow=false; underflow=false}) - - let logand ik x y = - let interval_logand = bit Ints_t.logand ik in - binop x y interval_logand - - let logor ik x y = - let interval_logor = bit Ints_t.logor ik in - binop x y interval_logor - - let logxor ik x y = - let interval_logxor = bit Ints_t.logxor ik in - binop x y interval_logxor - - let lognot ik x = - let interval_lognot i = - match interval_to_int i with - | Some x -> of_int ik (Ints_t.lognot x) |> fst - | _ -> top_of ik - in - unop x interval_lognot - - let shift_left ik x y = - let interval_shiftleft = bitcomp (fun x y -> Ints_t.shift_left x (Ints_t.to_int y)) ik in - binary_op_with_ovc x y interval_shiftleft - - let shift_right ik x y = - let interval_shiftright = bitcomp (fun x y -> Ints_t.shift_right x (Ints_t.to_int y)) ik in - binary_op_with_ovc x y interval_shiftright - - let c_lognot ik x = - let log1 f ik i1 = - match interval_to_bool i1 with - | Some x -> of_bool ik (f x) - | _ -> top_of ik - in - let interval_lognot = log1 not ik in - unop x interval_lognot - - let c_logand ik x y = - let interval_logand = log (&&) ik in - binop x y interval_logand - - let c_logor ik x y = - let interval_logor = log (||) ik in - binop x y interval_logor - - let add ?no_ov = binary_op_with_norm IArith.add - let sub ?no_ov = binary_op_with_norm IArith.sub - let mul ?no_ov = binary_op_with_norm IArith.mul - let neg ?no_ov = unary_op_with_norm IArith.neg - - let div ?no_ov ik x y = - let rec interval_div x (y1, y2) = begin - let top_of ik = top_of ik |> List.hd in - let is_zero v = v =. Ints_t.zero in - match y1, y2 with - | l, u when is_zero l && is_zero u -> top_of ik (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> interval_div x (Ints_t.one,y2) - | _, u when is_zero u -> interval_div x (y1, Ints_t.(neg one)) - | _ when leq (of_int ik (Ints_t.zero) |> fst) ([(y1,y2)]) -> top_of ik - | _ -> IArith.div x (y1, y2) - end - in binary_op_with_norm interval_div ik x y - - let rem ik x y = - let interval_rem (x, y) = - if Interval.is_top_of ik (Some x) && Interval.is_top_of ik (Some y) then - top_of ik - else - let (xl, xu) = x in let (yl, yu) = y in - let pos x = if x <. Ints_t.zero then Ints_t.neg x else x in - let b = (Ints_t.max (pos yl) (pos yu)) -. Ints_t.one in - let range = if xl >=. Ints_t.zero then (Ints_t.zero, Ints_t.min xu b) else (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit Ints_t.rem ik (x, y)) [range] - in - binop x y interval_rem - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik x = norm_intvs ~cast:true ik x - - (* - narrows down the extremeties of xs if they are equal to boundary values of the ikind with (possibly) narrower values from ys - *) - let narrow ik xs ys = match xs ,ys with - | [], _ -> [] | _ ,[] -> xs - | _, _ -> - let min_xs = minimal xs |> Option.get in - let max_xs = maximal xs |> Option.get in - let min_ys = minimal ys |> Option.get in - let max_ys = maximal ys |> Option.get in - let min_range,max_range = range ik in - let threshold = get_interval_threshold_widening () in - let min = if min_xs =. min_range || threshold && min_ys >. min_xs && IArith.is_lower_threshold min_xs then min_ys else min_xs in - let max = if max_xs =. max_range || threshold && max_ys <. max_xs && IArith.is_upper_threshold max_xs then max_ys else max_xs in - xs - |> (function (_, y)::z -> (min, y)::z | _ -> []) - |> List.rev - |> (function (x, _)::z -> (x, max)::z | _ -> []) - |> List.rev - - (* - 1. partitions the intervals of xs by assigning each of them to the an interval in ys that includes it. - and joins all intervals in xs assigned to the same interval in ys as one interval. - 2. checks for every pair of adjacent pairs whether the pairs did approach (if you compare the intervals from xs and ys) and merges them if it is the case. - 3. checks whether partitions at the extremeties are approaching infinity (and expands them to infinity. in that case) - - The expansion (between a pair of adjacent partitions or at extremeties ) stops at a threshold. - *) - let widen ik xs ys = - let (min_ik,max_ik) = range ik in - let threshold = get_bool "ana.int.interval_threshold_widening" in - let upper_threshold (_,u) = IArith.upper_threshold u max_ik in - let lower_threshold (l,_) = IArith.lower_threshold l min_ik in - (*obtain partitioning of xs intervals according to the ys interval that includes them*) - let rec interval_sets_to_partitions (ik: ikind) (acc : (int_t * int_t) option) (xs: t) (ys: t)= - match xs,ys with - | _, [] -> [] - | [], (y::ys) -> (acc,y):: interval_sets_to_partitions ik None [] ys - | (x::xs), (y::ys) when Interval.leq (Some x) (Some y) -> interval_sets_to_partitions ik (Interval.join ik acc (Some x)) xs (y::ys) - | (x::xs), (y::ys) -> (acc,y) :: interval_sets_to_partitions ik None (x::xs) ys - in - let interval_sets_to_partitions ik xs ys = interval_sets_to_partitions ik None xs ys in - (*merge a pair of adjacent partitions*) - let merge_pair ik (a,b) (c,d) = - let new_a = function - | None -> Some (upper_threshold b, upper_threshold b) - | Some (ax,ay) -> Some (ax, upper_threshold b) - in - let new_c = function - | None -> Some (lower_threshold d, lower_threshold d) - | Some (cx,cy) -> Some (lower_threshold d, cy) - in - if threshold && (lower_threshold d +. Ints_t.one) >. (upper_threshold b) then - [(new_a a,(fst b, upper_threshold b)); (new_c c, (lower_threshold d, snd d))] - else - [(Interval.join ik a c, (Interval.join ik (Some b) (Some d) |> Option.get))] - in - let partitions_are_approaching part_left part_right = match part_left, part_right with - | (Some (_, left_x), (_, left_y)), (Some (right_x, _), (right_y, _)) -> (right_x -. left_x) >. (right_y -. left_y) - | _,_ -> false - in - (*merge all approaching pairs of adjacent partitions*) - let rec merge_list ik = function - | [] -> [] - | x::y::xs when partitions_are_approaching x y -> merge_list ik ((merge_pair ik x y) @ xs) - | x::xs -> x :: merge_list ik xs - in - (*expands left extremity*) - let widen_left = function - | [] -> [] - | (None,(lb,rb))::ts -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (None, (lt,rb))::ts - | (Some (la,ra), (lb,rb))::ts when lb <. la -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (Some (la,ra),(lt,rb))::ts - | x -> x - in - (*expands right extremity*) - let widen_right x = - let map_rightmost = function - | [] -> [] - | (None,(lb,rb))::ts -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (None, (lb,ut))::ts - | (Some (la,ra), (lb,rb))::ts when ra <. rb -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (Some (la,ra),(lb,ut))::ts - | x -> x - in - List.rev x |> map_rightmost |> List.rev - in - interval_sets_to_partitions ik xs ys |> merge_list ik |> widen_left |> widen_right |> List.map snd - - let starting ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (fst (range ik), n) - - let invariant_ikind e ik xs = - List.map (fun x -> Interval.invariant_ikind e ik (Some x)) xs |> - let open Invariant in List.fold_left (||) (bot ()) - - let modulo n k = - let result = Ints_t.rem n k in - if result >=. Ints_t.zero then result - else result +. k - - let refine_with_congruence ik (intvs: t) (cong: (int_t * int_t ) option): t = - let refine_with_congruence_interval ik (cong : (int_t * int_t ) option) (intv : (int_t * int_t ) option): t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if m =. Ints_t.zero && (c <. x || c >. y) then [] - else if m =. Ints_t.zero then - [(c, c)] - else - let (min_ik, max_ik) = range ik in - let rcx = - if x =. min_ik then x else - x +. (modulo (c -. x) (Ints_t.abs m)) in - let lcy = - if y =. max_ik then y else - y -. (modulo (y -. c) (Ints_t.abs m)) in - if rcx >. lcy then [] - else if rcx =. lcy then norm_interval ik (rcx, rcx) |> fst - else norm_interval ik (rcx, lcy) |> fst - | _ -> [] - in - List.concat_map (fun x -> refine_with_congruence_interval ik cong (Some x)) intvs - - let refine_with_interval ik xs = function None -> [] | Some (a,b) -> meet ik xs [(a,b)] - - let refine_with_incl_list ik intvs = function - | None -> intvs - | Some xs -> meet ik intvs (List.map (fun x -> (x,x)) xs) - - let excl_range_to_intervalset (ik: ikind) ((min, max): int_t * int_t) (excl: int_t): t = - let intv1 = (min, excl -. Ints_t.one) in - let intv2 = (excl +. Ints_t.one, max) in - norm_intvs ik ~suppress_ovwarn:true [intv1 ; intv2] |> fst - - let of_excl_list ik (excls: int_t list) = - let excl_list = List.map (excl_range_to_intervalset ik (range ik)) excls in - let res = List.fold_left (meet ik) (top_of ik) excl_list in - res - - let refine_with_excl_list ik (intv : t) = function - | None -> intv - | Some (xs, range) -> - let excl_to_intervalset (ik: ikind) ((rl, rh): (int64 * int64)) (excl: int_t): t = - excl_range_to_intervalset ik (Ints_t.of_bigint (Size.min_from_bit_range rl),Ints_t.of_bigint (Size.max_from_bit_range rh)) excl - in - let excl_list = List.map (excl_to_intervalset ik range) xs in - List.fold_left (meet ik) intv excl_list - - let project ik p t = t - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let list_pair_arb = QCheck.small_list pair_arb in - let canonize_randomly_generated_list = (fun x -> norm_intvs ik x |> fst) in - let shrink xs = GobQCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list - in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) canonize_randomly_generated_list list_pair_arb) -end - -module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t = struct - include D - - let add ?no_ov ik x y = fst @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = fst @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = fst @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = fst @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = fst @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = fst @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = fst @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = fst @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = fst @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = fst @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = fst @@ D.shift_left ik x y - - let shift_right ik x y = fst @@ D.shift_right ik x y -end - -module IntIkind = struct let ikind () = Cil.IInt end -module Interval = IntervalFunctor (IntOps.BigIntOps) -module Interval32 = IntDomWithDefaultIkind (IntDomLifter (SOverflowUnlifter (IntervalFunctor (IntOps.Int64Ops)))) (IntIkind) -module IntervalSet = IntervalSetFunctor (IntOps.BigIntOps) -module Integers (Ints_t : IntOps.IntOps): IkindUnawareS with type t = Ints_t.t and type int_t = Ints_t.t = (* no top/bot, order is <= *) -struct - include Printable.Std - let name () = "integers" - type t = Ints_t.t [@@deriving eq, ord, hash] - type int_t = Ints_t.t - let top () = raise Unknown - let bot () = raise Error - let top_of ik = top () - let bot_of ik = bot () - let show (x: Ints_t.t) = Ints_t.to_string x - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - (* is_top and is_bot are never called, but if they were, the Std impl would raise their exception, so we overwrite them: *) - let is_top _ = false - let is_bot _ = false - - let equal_to i x = if i > x then `Neq else `Top - let leq x y = x <= y - let join x y = if Ints_t.compare x y > 0 then x else y - let widen = join - let meet x y = if Ints_t.compare x y > 0 then y else x - let narrow = meet - - let of_bool x = if x then Ints_t.one else Ints_t.zero - let to_bool' x = x <> Ints_t.zero - let to_bool x = Some (to_bool' x) - let of_int x = x - let to_int x = Some x - - let neg = Ints_t.neg - let add = Ints_t.add (* TODO: signed overflow is undefined behavior! *) - let sub = Ints_t.sub - let mul = Ints_t.mul - let div = Ints_t.div - let rem = Ints_t.rem - let lt n1 n2 = of_bool (n1 < n2) - let gt n1 n2 = of_bool (n1 > n2) - let le n1 n2 = of_bool (n1 <= n2) - let ge n1 n2 = of_bool (n1 >= n2) - let eq n1 n2 = of_bool (n1 = n2) - let ne n1 n2 = of_bool (n1 <> n2) - let lognot = Ints_t.lognot - let logand = Ints_t.logand - let logor = Ints_t.logor - let logxor = Ints_t.logxor - let shift_left n1 n2 = Ints_t.shift_left n1 (Ints_t.to_int n2) - let shift_right n1 n2 = Ints_t.shift_right n1 (Ints_t.to_int n2) - let c_lognot n1 = of_bool (not (to_bool' n1)) - let c_logand n1 n2 = of_bool ((to_bool' n1) && (to_bool' n2)) - let c_logor n1 n2 = of_bool ((to_bool' n1) || (to_bool' n2)) - let cast_to ?(suppress_ovwarn=false) ?torg t x = failwith @@ "Cast_to not implemented for " ^ (name ()) ^ "." - let arbitrary ik = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 (* TODO: use ikind *) - let invariant _ _ = Invariant.none (* TODO *) -end - -module FlatPureIntegers: IkindUnawareS with type t = int64 and type int_t = int64 = (* Integers, but raises Unknown/Error on join/meet *) -struct - include Integers(IntOps.Int64Ops) - let top () = raise Unknown - let bot () = raise Error - let leq = equal - let pretty_diff () (x,y) = Pretty.dprintf "Integer %a instead of %a" pretty x pretty y - let join x y = if equal x y then x else top () - let meet x y = if equal x y then x else bot () -end - -module Flat (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Lift, but goes to `Top/`Bot if Base raises Unknown/Error *) -struct - type int_t = Base.int_t - include Lattice.FlatConf (struct - include Printable.DefaultConf - let top_name = "Unknown int" - let bot_name = "Error int" - end) (Base) - - let top_of ik = top () - let bot_of ik = bot () - - - let name () = "flat integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ikind x = top_of ikind - let ending ?(suppress_ovwarn=false) ikind x = top_of ikind - let maximal x = None - let minimal x = None - - let lift1 f x = match x with - | `Lifted x -> - (try `Lifted (f x) with Unknown -> `Top | Error -> `Bot) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> - (try `Lifted (f x y) with Unknown -> `Top | Error -> `Bot) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Lift (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Flat, but does not go to `Top/Bot` if Base raises Unknown/Error *) -struct - include Lattice.LiftPO (struct - include Printable.DefaultConf - let top_name = "MaxInt" - let bot_name = "MinInt" - end) (Base) - type int_t = Base.int_t - let top_of ik = top () - let bot_of ik = bot () - include StdTop (struct type nonrec t = t let top_of = top_of end) - - let name () = "lifted integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let lift1 f x = match x with - | `Lifted x -> `Lifted (f x) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> `Lifted (f x y) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Flattened = Flat (Integers (IntOps.Int64Ops)) -module Lifted = Lift (Integers (IntOps.Int64Ops)) - -module Reverse (Base: IkindUnawareS) = -struct - include Base - include (Lattice.Reverse (Base) : Lattice.S with type t := Base.t) -end - -module BISet = struct - include SetDomain.Make (IntOps.BigIntOps) - let is_singleton s = cardinal s = 1 -end - -(* The module [Exclusion] constains common functionality about handling of exclusion sets between [DefExc] and [Enums] *) -module Exclusion = -struct - module R = Interval32 - (* We use these types for the functions in this module to make the intended meaning more explicit *) - type t = Exc of BISet.t * Interval32.t - type inc = Inc of BISet.t [@@unboxed] - let max_of_range r = Size.max_from_bit_range (Option.get (R.maximal r)) - let min_of_range r = Size.min_from_bit_range (Option.get (R.minimal r)) - let cardinality_of_range r = Z.succ (Z.add (Z.neg (min_of_range r)) (max_of_range r)) - - let cardinality_BISet s = - Z.of_int (BISet.cardinal s) - - let leq_excl_incl (Exc (xs, r)) (Inc ys) = - (* For a <= b to hold, the cardinalities must fit, i.e. |a| <= |b|, which implies |min_r, max_r| - |xs| <= |ys|. We check this first. *) - let lower_bound_cardinality_a = Z.sub (cardinality_of_range r) (cardinality_BISet xs) in - let card_b = cardinality_BISet ys in - if Z.compare lower_bound_cardinality_a card_b > 0 then - false - else (* The cardinality did fit, so we check for all elements that are represented by range r, whether they are in (xs union ys) *) - let min_a = min_of_range r in - let max_a = max_of_range r in - GobZ.for_all_range (fun el -> BISet.mem el xs || BISet.mem el ys) (min_a, max_a) - - let leq (Exc (xs, r)) (Exc (ys, s)) = - let min_a, max_a = min_of_range r, max_of_range r in - let excluded_check = BISet.for_all (fun y -> BISet.mem y xs || Z.compare y min_a < 0 || Z.compare y max_a > 0) ys in (* if true, then the values ys, that are not in b, also do not occur in a *) - if not excluded_check - then false - else begin (* Check whether all elements that are in the range r, but not in s, are in xs, i.e. excluded. *) - if R.leq r s then true - else begin if Z.compare (cardinality_BISet xs) (Z.sub (cardinality_of_range r) (cardinality_of_range s)) >= 0 (* Check whether the number of excluded elements in a is as least as big as |min_r, max_r| - |min_s, max_s| *) - then - let min_b, max_b = min_of_range s, max_of_range s in - let leq1 = (* check whether the elements in [r_l; s_l-1] are all in xs, i.e. excluded *) - if Z.compare min_a min_b < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (min_a, Z.pred min_b) - else - true - in - let leq2 () = (* check whether the elements in [s_u+1; r_u] are all in xs, i.e. excluded *) - if Z.compare max_b max_a < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (Z.succ max_b, max_a) - else - true - in - leq1 && (leq2 ()) - else - false - end - end -end - -module DefExc : S with type int_t = Z.t = (* definite or set of excluded values *) -struct - module S = BISet - module R = Interval32 (* range for exclusion *) - - (* Ikind used for intervals representing the domain *) - let range_ikind = Cil.IInt - let size t = R.of_interval range_ikind (let a,b = Size.bits_i64 t in Int64.neg a,b) - - - type t = [ - | `Excluded of S.t * R.t - | `Definite of Z.t - | `Bot - ] [@@deriving eq, ord, hash] - type int_t = Z.t - let name () = "def_exc" - - - let top_range = R.of_interval range_ikind (-99L, 99L) (* Since there is no top ikind we use a range that includes both ILongLong [-63,63] and IULongLong [0,64]. Only needed for intermediate range computation on longs. Correct range is set by cast. *) - let top () = `Excluded (S.empty (), top_range) - let bot () = `Bot - let top_of ik = `Excluded (S.empty (), size ik) - let bot_of ik = bot () - - let show x = - let short_size x = "("^R.show x^")" in - match x with - | `Bot -> "Error int" - | `Definite x -> Z.to_string x - (* Print the empty exclusion as if it was a distinct top element: *) - | `Excluded (s,l) when S.is_empty s -> "Unknown int" ^ short_size l - (* Prepend the exclusion sets with something: *) - | `Excluded (s,l) -> "Not " ^ S.show s ^ short_size l - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let maximal = function - | `Definite x -> Some x - | `Excluded (s,r) -> Some (Exclusion.max_of_range r) - | `Bot -> None - - let minimal = function - | `Definite x -> Some x - | `Excluded (s,r) -> Some (Exclusion.min_of_range r) - | `Bot -> None - - let in_range r i = - if Z.compare i Z.zero < 0 then - let lowerb = Exclusion.min_of_range r in - Z.compare lowerb i <= 0 - else - let upperb = Exclusion.max_of_range r in - Z.compare i upperb <= 0 - - let is_top x = x = top () - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Definite x -> if i = x then `Eq else `Neq - | `Excluded (s,r) -> if S.mem i s then `Neq else `Top - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik = function - | `Excluded (s,r) -> - let r' = size ik in - if R.leq r r' then (* upcast -> no change *) - `Excluded (s, r) - else if ik = IBool then (* downcast to bool *) - if S.mem Z.zero s then - `Definite Z.one - else - `Excluded (S.empty(), r') - else - (* downcast: may overflow *) - (* let s' = S.map (Size.cast ik) s in *) - (* We want to filter out all i in s' where (t)x with x in r could be i. *) - (* Since this is hard to compute, we just keep all i in s' which overflowed, since those are safe - all i which did not overflow may now be possible due to overflow of r. *) - (* S.diff s' s, r' *) - (* The above is needed for test 21/03, but not sound! See example https://github.com/goblint/analyzer/pull/95#discussion_r483023140 *) - `Excluded (S.empty (), r') - | `Definite x -> `Definite (Size.cast ik x) - | `Bot -> `Bot - - (* Wraps definite values and excluded values according to the ikind. - * For an `Excluded s,r , assumes that r is already an overapproximation of the range of possible values. - * r might be larger than the possible range of this type; the range of the returned `Excluded set will be within the bounds of the ikind. - *) - let norm ik v = - match v with - | `Excluded (s, r) -> - let possibly_overflowed = not (R.leq r (size ik)) || not (S.for_all (in_range (size ik)) s) in - (* If no overflow occurred, just return x *) - if not possibly_overflowed then ( - v - ) - (* Else, if an overflow might have occurred but we should just ignore it *) - else if should_ignore_overflow ik then ( - let r = size ik in - (* filter out excluded elements that are not in the range *) - let mapped_excl = S.filter (in_range r) s in - `Excluded (mapped_excl, r) - ) - (* Else, if an overflow occurred that we should not treat with wrap-around, go to top *) - else if not (should_wrap ik) then ( - top_of ik - ) else ( - (* Else an overflow occurred that we should treat with wrap-around *) - let r = size ik in - (* Perform a wrap-around for unsigned values and for signed values (if configured). *) - let mapped_excl = S.map (fun excl -> Size.cast ik excl) s in - match ik with - | IBool -> - begin match S.mem Z.zero mapped_excl, S.mem Z.one mapped_excl with - | false, false -> `Excluded (mapped_excl, r) (* Not {} -> Not {} *) - | true, false -> `Definite Z.one (* Not {0} -> 1 *) - | false, true -> `Definite Z.zero (* Not {1} -> 0 *) - | true, true -> `Bot (* Not {0, 1} -> bot *) - end - | ik -> - `Excluded (mapped_excl, r) - ) - | `Definite x -> - let min, max = Size.range ik in - (* Perform a wrap-around for unsigned values and for signed values (if configured). *) - if should_wrap ik then ( - cast_to ik v - ) - else if Z.compare min x <= 0 && Z.compare x max <= 0 then ( - v - ) - else if should_ignore_overflow ik then ( - M.warn ~category:M.Category.Integer.overflow "DefExc: Value was outside of range, indicating overflow, but 'sem.int.signed_overflow' is 'assume_none' -> Returned Bot"; - `Bot - ) - else ( - top_of ik - ) - | `Bot -> `Bot - - let leq x y = match (x,y) with - (* `Bot <= x is always true *) - | `Bot, _ -> true - (* Anything except bot <= bot is always false *) - | _, `Bot -> false - (* Two known values are leq whenever equal *) - | `Definite (x: int_t), `Definite y -> x = y - (* A definite value is leq all exclusion sets that don't contain it *) - | `Definite x, `Excluded (s,r) -> in_range r x && not (S.mem x s) - (* No finite exclusion set can be leq than a definite value *) - | `Excluded (xs, xr), `Definite d -> - Exclusion.(leq_excl_incl (Exc (xs, xr)) (Inc (S.singleton d))) - | `Excluded (xs,xr), `Excluded (ys,yr) -> - Exclusion.(leq (Exc (xs,xr)) (Exc (ys, yr))) - - let join' ?range ik x y = - match (x,y) with - (* The least upper bound with the bottom element: *) - | `Bot, x -> x - | x, `Bot -> x - (* The case for two known values: *) - | `Definite (x: int_t), `Definite y -> - (* If they're equal, it's just THAT value *) - if x = y then `Definite x - (* Unless one of them is zero, we can exclude it: *) - else - let a,b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval range_ikind a) (R.of_interval range_ikind b) in - `Excluded ((if Z.equal x Z.zero || Z.equal y Z.zero then S.empty () else S.singleton Z.zero), r) - (* A known value and an exclusion set... the definite value should no - * longer be excluded: *) - | `Excluded (s,r), `Definite x - | `Definite x, `Excluded (s,r) -> - if not (in_range r x) then - let a = R.of_interval range_ikind (Size.min_range_sign_agnostic x) in - `Excluded (S.remove x s, R.join a r) - else - `Excluded (S.remove x s, r) - (* For two exclusion sets, only their intersection can be excluded: *) - | `Excluded (x,wx), `Excluded (y,wy) -> `Excluded (S.inter x y, range |? R.join wx wy) - - let join ik = join' ik - - - let widen ik x y = - if get_def_exc_widen_by_join () then - join' ik x y - else if equal x y then - x - else - join' ~range:(size ik) ik x y - - - let meet ik x y = - match (x,y) with - (* Greatest LOWER bound with the least element is trivial: *) - | `Bot, _ -> `Bot - | _, `Bot -> `Bot - (* Definite elements are either equal or the glb is bottom *) - | `Definite x, `Definite y -> if x = y then `Definite x else `Bot - (* The glb of a definite element and an exclusion set is either bottom or - * just the element itself, if it isn't in the exclusion set *) - | `Excluded (s,r), `Definite x - | `Definite x, `Excluded (s,r) -> if S.mem x s || not (in_range r x) then `Bot else `Definite x - (* The greatest lower bound of two exclusion sets is their union, this is - * just DeMorgans Law *) - | `Excluded (x,r1), `Excluded (y,r2) -> - let r' = R.meet r1 r2 in - let s' = S.union x y |> S.filter (in_range r') in - `Excluded (s', r') - - let narrow ik x y = x - - let of_int ik x = norm ik @@ `Definite x - let to_int x = match x with - | `Definite x -> Some x - | _ -> None - - let from_excl ikind (s: S.t) = norm ikind @@ `Excluded (s, size ikind) - - let of_bool_cmp ik x = of_int ik (if x then Z.one else Z.zero) - let of_bool = of_bool_cmp - let to_bool x = - match x with - | `Definite x -> Some (IntOps.BigIntOps.to_bool x) - | `Excluded (s,r) when S.mem Z.zero s -> Some true - | _ -> None - let top_bool = `Excluded (S.empty (), R.of_interval range_ikind (0L, 1L)) - - let of_interval ?(suppress_ovwarn=false) ik (x,y) = - if Z.compare x y = 0 then - of_int ik x - else - let a, b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval ~suppress_ovwarn range_ikind a) (R.of_interval ~suppress_ovwarn range_ikind b) in - let ex = if Z.gt x Z.zero || Z.lt y Z.zero then S.singleton Z.zero else S.empty () in - norm ik @@ (`Excluded (ex, r)) - - let starting ?(suppress_ovwarn=false) ikind x = - let _,u_ik = Size.range ikind in - of_interval ~suppress_ovwarn ikind (x, u_ik) - - let ending ?(suppress_ovwarn=false) ikind x = - let l_ik,_ = Size.range ikind in - of_interval ~suppress_ovwarn ikind (l_ik, x) - - let of_excl_list t l = - let r = size t in (* elements in l are excluded from the full range of t! *) - `Excluded (List.fold_right S.add l (S.empty ()), r) - let is_excl_list l = match l with `Excluded _ -> true | _ -> false - let to_excl_list (x:t) = match x with - | `Definite _ -> None - | `Excluded (s,r) -> Some (S.elements s, (Option.get (R.minimal r), Option.get (R.maximal r))) - | `Bot -> None - - let to_incl_list x = match x with - | `Definite x -> Some [x] - | `Excluded _ -> None - | `Bot -> None - - let apply_range f r = (* apply f to the min/max of the old range r to get a new range *) - (* If the Int64 might overflow on us during computation, we instead go to top_range *) - match R.minimal r, R.maximal r with - | _ -> - let rf m = (size % Size.min_for % f) (m r) in - let r1, r2 = rf Exclusion.min_of_range, rf Exclusion.max_of_range in - R.join r1 r2 - - (* Default behaviour for unary operators, simply maps the function to the - * DefExc data structure. *) - let lift1 f ik x = norm ik @@ match x with - | `Excluded (s,r) -> - let s' = S.map f s in - `Excluded (s', apply_range f r) - | `Definite x -> `Definite (f x) - | `Bot -> `Bot - - let lift2 f ik x y = norm ik (match x,y with - (* We don't bother with exclusion sets: *) - | `Excluded _, `Definite _ - | `Definite _, `Excluded _ - | `Excluded _, `Excluded _ -> top () - (* The good case: *) - | `Definite x, `Definite y -> - (try `Definite (f x y) with | Division_by_zero -> top ()) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) - - (* Default behaviour for binary operators that are injective in either - * argument, so that Exclusion Sets can be used: *) - let lift2_inj f ik x y = - let def_exc f x s r = `Excluded (S.map (f x) s, apply_range (f x) r) in - norm ik @@ - match x,y with - (* If both are exclusion sets, there isn't anything we can do: *) - | `Excluded _, `Excluded _ -> top () - (* A definite value should be applied to all members of the exclusion set *) - | `Definite x, `Excluded (s,r) -> def_exc f x s r - (* Same thing here, but we should flip the operator to map it properly *) - | `Excluded (s,r), `Definite x -> def_exc (Batteries.flip f) x s r - (* The good case: *) - | `Definite x, `Definite y -> `Definite (f x y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - (* The equality check: *) - let eq ik x y = match x,y with - (* Not much to do with two exclusion sets: *) - | `Excluded _, `Excluded _ -> top () - (* Is x equal to an exclusion set, if it is a member then NO otherwise we - * don't know: *) - | `Definite x, `Excluded (s,r) -> if S.mem x s then of_bool IInt false else top () - | `Excluded (s,r), `Definite x -> if S.mem x s then of_bool IInt false else top () - (* The good case: *) - | `Definite x, `Definite y -> of_bool IInt (x = y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - (* The inequality check: *) - let ne ik x y = match x,y with - (* Not much to do with two exclusion sets: *) - | `Excluded _, `Excluded _ -> top () - (* Is x unequal to an exclusion set, if it is a member then Yes otherwise we - * don't know: *) - | `Definite x, `Excluded (s,r) -> if S.mem x s then of_bool IInt true else top () - | `Excluded (s,r), `Definite x -> if S.mem x s then of_bool IInt true else top () - (* The good case: *) - | `Definite x, `Definite y -> of_bool IInt (x <> y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - let neg ?no_ov ik (x :t) = norm ik @@ lift1 Z.neg ik x - let add ?no_ov ik x y = norm ik @@ lift2_inj Z.add ik x y - - let sub ?no_ov ik x y = norm ik @@ lift2_inj Z.sub ik x y - let mul ?no_ov ik x y = norm ik @@ match x, y with - | `Definite z, (`Excluded _ | `Definite _) when Z.equal z Z.zero -> x - | (`Excluded _ | `Definite _), `Definite z when Z.equal z Z.zero -> y - | `Definite a, `Excluded (s,r) - (* Integer multiplication with even numbers is not injective. *) - (* Thus we cannot exclude the values to which the exclusion set would be mapped to. *) - | `Excluded (s,r),`Definite a when Z.equal (Z.rem a (Z.of_int 2)) Z.zero -> `Excluded (S.empty (), apply_range (Z.mul a) r) - | _ -> lift2_inj Z.mul ik x y - let div ?no_ov ik x y = lift2 Z.div ik x y - let rem ik x y = lift2 Z.rem ik x y - - (* Comparison handling copied from Enums. *) - let handle_bot x y f = match x, y with - | `Bot, `Bot -> `Bot - | `Bot, _ - | _, `Bot -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> f () - - let lt ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 < 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 >= 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let gt ik x y = lt ik y x - - let le ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 <= 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 > 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let ge ik x y = le ik y x - - let lognot = lift1 Z.lognot - - let logand ik x y = norm ik (match x,y with - (* We don't bother with exclusion sets: *) - | `Excluded _, `Definite i -> - (* Except in two special cases *) - if Z.equal i Z.zero then - `Definite Z.zero - else if Z.equal i Z.one then - of_interval IBool (Z.zero, Z.one) - else - top () - | `Definite _, `Excluded _ - | `Excluded _, `Excluded _ -> top () - (* The good case: *) - | `Definite x, `Definite y -> - (try `Definite (Z.logand x y) with | Division_by_zero -> top ()) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) - - - let logor = lift2 Z.logor - let logxor = lift2 Z.logxor - - let shift (shift_op: int_t -> int -> int_t) (ik: Cil.ikind) (x: t) (y: t) = - (* BigInt only accepts int as second argument for shifts; perform conversion here *) - let shift_op_big_int a (b: int_t) = - let (b : int) = Z.to_int b in - shift_op a b - in - (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = GobOption.for_all (fun x -> Z.lt x Z.zero) in - if is_negative (minimal x) || is_negative (minimal y) then - top_of ik - else - norm ik @@ lift2 shift_op_big_int ik x y - - let shift_left = - shift Z.shift_left - - let shift_right = - shift Z.shift_right - (* TODO: lift does not treat Not {0} as true. *) - let c_logand ik x y = - match to_bool x, to_bool y with - | Some false, _ - | _, Some false -> - of_bool ik false - | _, _ -> - lift2 IntOps.BigIntOps.c_logand ik x y - let c_logor ik x y = - match to_bool x, to_bool y with - | Some true, _ - | _, Some true -> - of_bool ik true - | _, _ -> - lift2 IntOps.BigIntOps.c_logor ik x y - let c_lognot ik = eq ik (of_int ik Z.zero) - - let invariant_ikind e ik (x:t) = - match x with - | `Definite x -> - IntInvariant.of_int e ik x - | `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 ri = IntInvariant.of_interval e ik (rmin, rmax) in - let si = IntInvariant.of_excl_list e ik (S.elements s) in - Invariant.(ri && si) - | `Bot -> Invariant.none - - let arbitrary ik = - let open QCheck.Iter in - let excluded s = from_excl ik s in - let definite x = of_int ik x in - let shrink = function - | `Excluded (s, _) -> GobQCheck.shrink (S.arbitrary ()) s >|= excluded (* S TODO: possibly shrink excluded to definite *) - | `Definite x -> (return `Bot) <+> (GobQCheck.shrink (IntOps.BigIntOps.arbitrary ()) x >|= definite) - | `Bot -> empty - in - QCheck.frequency ~shrink ~print:show [ - 20, QCheck.map excluded (S.arbitrary ()); - 10, QCheck.map definite (IntOps.BigIntOps.arbitrary ()); - 1, QCheck.always `Bot - ] (* S TODO: decide frequencies *) - - let refine_with_congruence ik a b = a - let refine_with_interval ik a b = match a, b with - | x, Some(i) -> meet ik x (of_interval ik i) - | _ -> a - let refine_with_excl_list ik a b = match a, b with - | `Excluded (s, r), Some(ls, _) -> meet ik (`Excluded (s, r)) (of_excl_list ik ls) (* TODO: refine with excl range? *) - | _ -> a - let refine_with_incl_list ik a b = a - - let project ik p t = t -end - -(* Inclusion/Exclusion sets. Go to top on arithmetic operations (except for some easy cases, e.g. multiplication with 0). Joins on widen, i.e. precise integers as long as not derived from arithmetic expressions. *) -module Enums : S with type int_t = Z.t = struct - module R = Interval32 (* range for exclusion *) - - let range_ikind = Cil.IInt - let size t = R.of_interval range_ikind (let a,b = Size.bits_i64 t in Int64.neg a,b) - - type t = Inc of BISet.t | Exc of BISet.t * R.t [@@deriving eq, ord, hash] (* inclusion/exclusion set *) - - type int_t = Z.t - let name () = "enums" - let bot () = failwith "bot () not implemented for Enums" - let top () = failwith "top () not implemented for Enums" - let bot_of ik = Inc (BISet.empty ()) - let top_bool = Inc (BISet.of_list [Z.zero; Z.one]) - let top_of ik = - match ik with - | IBool -> top_bool - | _ -> Exc (BISet.empty (), size ik) - - let range ik = Size.range ik - -(* - let max_of_range r = Size.max_from_bit_range (Option.get (R.maximal r)) - let min_of_range r = Size.min_from_bit_range (Option.get (R.minimal r)) - let cardinality_of_range r = Z.add (Z.neg (min_of_range r)) (max_of_range r) *) - let value_in_range (min, max) v = Z.compare min v <= 0 && Z.compare v max <= 0 - - let show = function - | Inc xs when BISet.is_empty xs -> "bot" - | Inc xs -> "{" ^ (String.concat ", " (List.map Z.to_string (BISet.elements xs))) ^ "}" - | Exc (xs,r) -> "not {" ^ (String.concat ", " (List.map Z.to_string (BISet.elements xs))) ^ "} " ^ "("^R.show r^")" - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - (* Normalization function for enums, that handles overflows for Inc. - As we do not compute on Excl, we do not have to perform any overflow handling for it. *) - let norm ikind v = - let min, max = range ikind in - (* Whether the value v lies within the values of the specified ikind. *) - let value_in_ikind v = - Z.compare min v <= 0 && Z.compare v max <= 0 - in - match v with - | Inc xs when BISet.for_all value_in_ikind xs -> v - | Inc xs -> - if should_wrap ikind then - Inc (BISet.map (Size.cast ikind) xs) - else if should_ignore_overflow ikind then - Inc (BISet.filter value_in_ikind xs) - else - top_of ikind - | Exc (xs, r) -> - (* The following assert should hold for Exc, therefore we do not have to overflow handling / normalization for it: - let range_in_ikind r = - R.leq r (size ikind) - in - let r_min, r_max = min_of_range r, max_of_range r in - assert (range_in_ikind r && BISet.for_all (value_in_range (r_min, r_max)) xs); *) - begin match ikind with - | IBool -> - begin match BISet.mem Z.zero xs, BISet.mem Z.one xs with - | false, false -> top_bool (* Not {} -> {0, 1} *) - | true, false -> Inc (BISet.singleton Z.one) (* Not {0} -> {1} *) - | false, true -> Inc (BISet.singleton Z.zero) (* Not {1} -> {0} *) - | true, true -> bot_of ikind (* Not {0, 1} -> bot *) - end - | _ -> - v - end - - - let equal_to i = function - | Inc x -> - if BISet.mem i x then - if BISet.is_singleton x then `Eq - else `Top - else `Neq - | Exc (x, r) -> - if BISet.mem i x then `Neq - else `Top - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik v = norm ik @@ match v with - | Exc (s,r) -> - let r' = size ik in - if R.leq r r' then (* upcast -> no change *) - Exc (s, r) - else if ik = IBool then (* downcast to bool *) - if BISet.mem Z.zero s then - Inc (BISet.singleton Z.one) - else - Exc (BISet.empty(), r') - else (* downcast: may overflow *) - Exc ((BISet.empty ()), r') - | Inc xs -> - let casted_xs = BISet.map (Size.cast ik) xs in - if Cil.isSigned ik && not (BISet.equal xs casted_xs) - then top_of ik (* When casting into a signed type and the result does not fit, the behavior is implementation-defined *) - else Inc casted_xs - - let of_int ikind x = cast_to ikind (Inc (BISet.singleton x)) - - let of_interval ?(suppress_ovwarn=false) ik (x, y) = - if Z.compare x y = 0 then - of_int ik x - else - let a, b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval ~suppress_ovwarn range_ikind a) (R.of_interval ~suppress_ovwarn range_ikind b) in - let ex = if Z.gt x Z.zero || Z.lt y Z.zero then BISet.singleton Z.zero else BISet.empty () in - norm ik @@ (Exc (ex, r)) - - let join _ x y = - match x, y with - | Inc x, Inc y -> Inc (BISet.union x y) - | Exc (x,r1), Exc (y,r2) -> Exc (BISet.inter x y, R.join r1 r2) - | Exc (x,r), Inc y - | Inc y, Exc (x,r) -> - let r = if BISet.is_empty y - then r - else - let (min_el_range, max_el_range) = Batteries.Tuple2.mapn (fun x -> R.of_interval range_ikind (Size.min_range_sign_agnostic x)) (BISet.min_elt y, BISet.max_elt y) in - let range = R.join min_el_range max_el_range in - R.join r range - in - Exc (BISet.diff x y, r) - - let meet _ x y = - match x, y with - | Inc x, Inc y -> Inc (BISet.inter x y) - | Exc (x,r1), Exc (y,r2) -> - let r = R.meet r1 r2 in - let r_min, r_max = Exclusion.min_of_range r, Exclusion.max_of_range r in - let filter_by_range = BISet.filter (value_in_range (r_min, r_max)) in - (* We remove those elements from the exclusion set that do not fit in the range anyway *) - let excl = BISet.union (filter_by_range x) (filter_by_range y) in - Exc (excl, r) - | Inc x, Exc (y,r) - | Exc (y,r), Inc x -> Inc (BISet.diff x y) - - let widen = join - let narrow = meet - let leq a b = - match a, b with - | Inc xs, Exc (ys, r) -> - if BISet.is_empty xs - then true - else - let min_b, max_b = Exclusion.min_of_range r, Exclusion.max_of_range r in - let min_a, max_a = BISet.min_elt xs, BISet.max_elt xs in - (* Check that the xs fit into the range r *) - Z.compare min_b min_a <= 0 && Z.compare max_a max_b <= 0 && - (* && check that none of the values contained in xs is excluded, i.e. contained in ys. *) - BISet.for_all (fun x -> not (BISet.mem x ys)) xs - | Inc xs, Inc ys -> - BISet.subset xs ys - | Exc (xs, r), Exc (ys, s) -> - Exclusion.(leq (Exc (xs, r)) (Exc (ys, s))) - | Exc (xs, r), Inc ys -> - Exclusion.(leq_excl_incl (Exc (xs, r)) (Inc ys)) - - let handle_bot x y f = match is_bot x, is_bot y with - | false, false -> f () - | true, false - | false, true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | true, true -> Inc (BISet.empty ()) - - let lift1 f ikind v = norm ikind @@ match v with - | Inc x when BISet.is_empty x -> v (* Return bottom when value is bottom *) - | Inc x when BISet.is_singleton x -> Inc (BISet.singleton (f (BISet.choose x))) - | _ -> top_of ikind - - let lift2 f (ikind: Cil.ikind) u v = - handle_bot u v (fun () -> - norm ikind @@ match u, v with - | Inc x,Inc y when BISet.is_singleton x && BISet.is_singleton y -> Inc (BISet.singleton (f (BISet.choose x) (BISet.choose y))) - | _,_ -> top_of ikind) - - let lift2 f ikind a b = - try lift2 f ikind a b with Division_by_zero -> top_of ikind - - let neg ?no_ov = lift1 Z.neg - let add ?no_ov ikind a b = - match a, b with - | Inc z,x when BISet.is_singleton z && BISet.choose z = Z.zero -> x - | x,Inc z when BISet.is_singleton z && BISet.choose z = Z.zero -> x - | x,y -> lift2 Z.add ikind x y - let sub ?no_ov = lift2 Z.sub - let mul ?no_ov ikind a b = - match a, b with - | Inc one,x when BISet.is_singleton one && BISet.choose one = Z.one -> x - | x,Inc one when BISet.is_singleton one && BISet.choose one = Z.one -> x - | Inc zero,_ when BISet.is_singleton zero && BISet.choose zero = Z.zero -> a - | _,Inc zero when BISet.is_singleton zero && BISet.choose zero = Z.zero -> b - | x,y -> lift2 Z.mul ikind x y - - let div ?no_ov ikind a b = match a, b with - | x,Inc one when BISet.is_singleton one && BISet.choose one = Z.one -> x - | _,Inc zero when BISet.is_singleton zero && BISet.choose zero = Z.zero -> top_of ikind - | Inc zero,_ when BISet.is_singleton zero && BISet.choose zero = Z.zero -> a - | x,y -> lift2 Z.div ikind x y - - let rem = lift2 Z.rem - - let lognot = lift1 Z.lognot - let logand = lift2 Z.logand - let logor = lift2 Z.logor - let logxor = lift2 Z.logxor - - let shift (shift_op: int_t -> int -> int_t) (ik: Cil.ikind) (x: t) (y: t) = - handle_bot x y (fun () -> - (* BigInt only accepts int as second argument for shifts; perform conversion here *) - let shift_op_big_int a (b: int_t) = - let (b : int) = Z.to_int b in - shift_op a b - in - (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = GobOption.for_all (fun x -> Z.lt x Z.zero) in - if is_negative (minimal x) || is_negative (minimal y) then - top_of ik - else - lift2 shift_op_big_int ik x y) - - let shift_left = - shift Z.shift_left - - let shift_right = - shift Z.shift_right - - let of_bool ikind x = Inc (BISet.singleton (if x then Z.one else Z.zero)) - let to_bool = function - | Inc e when BISet.is_empty e -> None - | Exc (e,_) when BISet.is_empty e -> None - | Inc zero when BISet.is_singleton zero && BISet.choose zero = Z.zero -> Some false - | Inc xs when BISet.for_all ((<>) Z.zero) xs -> Some true - | Exc (xs,_) when BISet.exists ((=) Z.zero) xs -> Some true - | _ -> None - let to_int = function Inc x when BISet.is_singleton x -> Some (BISet.choose x) | _ -> None - - let to_excl_list = function Exc (x,r) when not (BISet.is_empty x) -> Some (BISet.elements x, (Option.get (R.minimal r), Option.get (R.maximal r))) | _ -> None - let of_excl_list ik xs = - let min_ik, max_ik = Size.range ik in - let exc = BISet.of_list @@ List.filter (value_in_range (min_ik, max_ik)) xs in - norm ik @@ Exc (exc, size ik) - let is_excl_list = BatOption.is_some % to_excl_list - let to_incl_list = function Inc s when not (BISet.is_empty s) -> Some (BISet.elements s) | _ -> None - - let starting ?(suppress_ovwarn=false) ikind x = - let _,u_ik = Size.range ikind in - of_interval ~suppress_ovwarn ikind (x, u_ik) - - let ending ?(suppress_ovwarn=false) ikind x = - let l_ik,_ = Size.range ikind in - of_interval ~suppress_ovwarn ikind (l_ik, x) - - let c_lognot ik x = - if is_bot x - then x - else - match to_bool x with - | Some b -> of_bool ik (not b) - | None -> top_bool - - let c_logand = lift2 IntOps.BigIntOps.c_logand - let c_logor = lift2 IntOps.BigIntOps.c_logor - let maximal = function - | Inc xs when not (BISet.is_empty xs) -> Some (BISet.max_elt xs) - | Exc (excl,r) -> - let rec decrement_while_contained v = - if BISet.mem v excl - then decrement_while_contained (Z.pred v) - else v - in - let range_max = Exclusion.max_of_range r in - Some (decrement_while_contained range_max) - | _ (* bottom case *) -> None - - let minimal = function - | Inc xs when not (BISet.is_empty xs) -> Some (BISet.min_elt xs) - | Exc (excl,r) -> - let rec increment_while_contained v = - if BISet.mem v excl - then increment_while_contained (Z.succ v) - else v - in - let range_min = Exclusion.min_of_range r in - Some (increment_while_contained range_min) - | _ (* bottom case *) -> None - - let lt ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 < 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 >= 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let gt ik x y = lt ik y x - - let le ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 <= 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 > 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let ge ik x y = le ik y x - - let eq ik x y = - handle_bot x y (fun () -> - match x, y with - | Inc xs, Inc ys when BISet.is_singleton xs && BISet.is_singleton ys -> of_bool ik (Z.equal (BISet.choose xs) (BISet.choose ys)) - | _, _ -> - if is_bot (meet ik x y) then - (* If the meet is empty, there is no chance that concrete values are equal *) - of_bool ik false - else - top_bool) - - let ne ik x y = c_lognot ik (eq ik x y) - - let invariant_ikind e ik x = - match x with - | Inc ps -> - IntInvariant.of_incl_list e ik (BISet.elements ps) - | Exc (ns, 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 ri = IntInvariant.of_interval e ik (rmin, rmax) in - let nsi = IntInvariant.of_excl_list e ik (BISet.elements ns) in - Invariant.(ri && nsi) - - - let arbitrary ik = - let open QCheck.Iter in - let neg s = of_excl_list ik (BISet.elements s) in - let pos s = norm ik (Inc s) in - let shrink = function - | Exc (s, _) -> GobQCheck.shrink (BISet.arbitrary ()) s >|= neg (* S TODO: possibly shrink neg to pos *) - | Inc s -> GobQCheck.shrink (BISet.arbitrary ()) s >|= pos - in - QCheck.frequency ~shrink ~print:show [ - 20, QCheck.map neg (BISet.arbitrary ()); - 10, QCheck.map pos (BISet.arbitrary ()); - ] (* S TODO: decide frequencies *) - - let refine_with_congruence ik a b = - let contains c m x = if Z.equal m Z.zero then Z.equal c x else Z.equal (Z.rem (Z.sub x c) m) Z.zero in - match a, b with - | Inc e, None -> bot_of ik - | Inc e, Some (c, m) -> Inc (BISet.filter (contains c m) e) - | _ -> a - - let refine_with_interval ik a b = a (* TODO: refine inclusion (exclusion?) set *) - - let refine_with_excl_list ik a b = - match b with - | Some (ls, _) -> meet ik a (of_excl_list ik ls) (* TODO: refine with excl range? *) - | _ -> a - - let refine_with_incl_list ik a b = - match a, b with - | Inc x, Some (ls) -> meet ik (Inc x) (Inc (BISet.of_list ls)) - | _ -> a - - let project ik p t = t -end module Congruence : S with type int_t = Z.t and type t = (Z.t * Z.t) option = struct @@ -3235,35 +493,3 @@ struct let project ik p t = t end - -module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct - - include D - - let lift v = (v, {overflow=false; underflow=false}) - - let add ?no_ov ik x y = lift @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = lift @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = lift @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = lift @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = lift @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = lift @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = lift @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = lift @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = lift @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = lift @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = lift @@ D.shift_left ik x y - - let shift_right ik x y = lift @@ D.shift_right ik x y - -end From 48fad68eb50a15778ad55dce114c15f65b5f4138 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 16:56:51 +0300 Subject: [PATCH 463/566] Remove Congruence from IntDomain0 --- src/cdomain/value/cdomains/intDomain0.ml | 493 ----------------------- 1 file changed, 493 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/intDomain0.ml index 7450e8a212..8dd8b07f74 100644 --- a/src/cdomain/value/cdomains/intDomain0.ml +++ b/src/cdomain/value/cdomains/intDomain0.ml @@ -2743,499 +2743,6 @@ module Enums : S with type int_t = Z.t = struct let project ik p t = t end -module Congruence : S with type int_t = Z.t and type t = (Z.t * Z.t) option = -struct - let name () = "congruences" - type int_t = Z.t - - (* represents congruence class of c mod m, None is bot *) - type t = (Z.t * Z.t) option [@@deriving eq, ord, hash] - - let ( *: ) = Z.mul - let (+:) = Z.add - let (-:) = Z.sub - let (%:) = Z.rem - let (/:) = Z.div - let (=:) = Z.equal - let (<:) x y = Z.compare x y < 0 - let (>:) x y = Z.compare x y > 0 - let (<=:) x y = Z.compare x y <= 0 - let (>=:) x y = Z.compare x y >= 0 - (* a divides b *) - let ( |: ) a b = - if a =: Z.zero then false else (b %: a) =: Z.zero - - let normalize ik x = - match x with - | None -> None - | Some (c, m) -> - if m =: Z.zero then - if should_wrap ik then - Some (Size.cast ik c, m) - else - Some (c, m) - else - let m' = Z.abs m in - let c' = c %: m' in - if c' <: Z.zero then - Some (c' +: m', m') - else - Some (c' %: m', m') - - let range ik = Size.range ik - - let top () = Some (Z.zero, Z.one) - let top_of ik = Some (Z.zero, Z.one) - let bot () = None - let bot_of ik = bot () - - let show = function ik -> match ik with - | None -> "⟂" - | Some (c, m) when (c, m) = (Z.zero, Z.zero) -> Z.to_string c - | Some (c, m) -> - let a = if c =: Z.zero then "" else Z.to_string c in - let b = if m =: Z.zero then "" else if m = Z.one then "ℤ" else Z.to_string m^"ℤ" in - let c = if a = "" || b = "" then "" else "+" in - a^c^b - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let is_top x = x = top () - - let equal_to i = function - | None -> failwith "unsupported: equal_to with bottom" - | Some (a, b) when b =: Z.zero -> if a =: i then `Eq else `Neq - | Some (a, b) -> if i %: b =: a then `Top else `Neq - - let leq (x:t) (y:t) = - match x, y with - | None, _ -> true - | Some _, None -> false - | Some (c1,m1), Some (c2,m2) when m2 =: Z.zero && m1 =: Z.zero -> c1 =: c2 - | Some (c1,m1), Some (c2,m2) when m2 =: Z.zero -> c1 =: c2 && m1 =: Z.zero - | Some (c1,m1), Some (c2,m2) -> m2 |: Z.gcd (c1 -: c2) m1 - (* Typo in original equation of P. Granger (m2 instead of m1): gcd (c1 -: c2) m2 - Reference: https://doi.org/10.1080/00207168908803778 Page 171 corollary 3.3*) - - let leq x y = - let res = leq x y in - if M.tracing then M.trace "congruence" "leq %a %a -> %a " pretty x pretty y pretty (Some (Z.of_int (Bool.to_int res), Z.zero)) ; - res - - let join ik (x:t) y = - match x, y with - | None, z | z, None -> z - | Some (c1,m1), Some (c2,m2) -> - let m3 = Z.gcd m1 (Z.gcd m2 (c1 -: c2)) in - normalize ik (Some (c1, m3)) - - let join ik (x:t) y = - let res = join ik x y in - if M.tracing then M.trace "congruence" "join %a %a -> %a" pretty x pretty y pretty res; - res - - - let meet ik x y = - (* if it exists, c2/a2 is solution to a*x ≡ c (mod m) *) - let congruence_series a c m = - let rec next a1 c1 a2 c2 = - if a2 |: a1 then (a2, c2) - else next a2 c2 (a1 %: a2) (c1 -: (c2 *: (a1 /: a2))) - in next m Z.zero a c - in - let simple_case i c m = - if m |: (i -: c) - then Some (i, Z.zero) else None - in - match x, y with - | Some (c1, m1), Some (c2, m2) when m1 =: Z.zero && m2 =: Z.zero -> if c1 =: c2 then Some (c1, Z.zero) else None - | Some (c1, m1), Some (c2, m2) when m1 =: Z.zero -> simple_case c1 c2 m2 - | Some (c1, m1), Some (c2, m2) when m2 =: Z.zero -> simple_case c2 c1 m1 - | Some (c1, m1), Some (c2, m2) when (Z.gcd m1 m2) |: (c1 -: c2) -> - let (c, m) = congruence_series m1 (c2 -: c1 ) m2 in - normalize ik (Some(c1 +: (m1 *: (m /: c)), m1 *: (m2 /: c))) - | _ -> None - - let meet ik x y = - let res = meet ik x y in - if M.tracing then M.trace "congruence" "meet %a %a -> %a" pretty x pretty y pretty res; - res - - let to_int = function Some (c, m) when m =: Z.zero -> Some c | _ -> None - let of_int ik (x: int_t) = normalize ik @@ Some (x, Z.zero) - let zero = Some (Z.zero, Z.zero) - let one = Some (Z.one, Z.zero) - let top_bool = top() - - let of_bool _ik = function true -> one | false -> zero - - let to_bool (a: t) = match a with - | None -> None - | x when equal zero x -> Some false - | x -> if leq zero x then None else Some true - - let starting ?(suppress_ovwarn=false) ik n = top() - - let ending = starting - - let of_congruence ik (c,m) = normalize ik @@ Some(c,m) - - let maximal t = match t with - | Some (x, y) when y =: Z.zero -> Some x - | _ -> None - - let minimal t = match t with - | Some (x,y) when y =: Z.zero -> Some x - | _ -> None - - (* cast from original type to ikind, set to top if the value doesn't fit into the new type *) - let cast_to ?(suppress_ovwarn=false) ?torg ?(no_ov=false) t x = - match x with - | None -> None - | Some (c, m) when m =: Z.zero -> - let c' = Size.cast t c in - (* When casting into a signed type and the result does not fit, the behavior is implementation-defined. (C90 6.2.1.2, C99 and C11 6.3.1.3) *) - (* We go with GCC behavior here: *) - (* For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised. *) - (* (https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html) *) - (* Clang behaves the same but they never document that anywhere *) - Some (c', m) - | _ -> - let (min_t, max_t) = range t in - let p ikorg = - let (min_ikorg, max_ikorg) = range ikorg in - ikorg = t || (max_t >=: max_ikorg && min_t <=: min_ikorg) - in - match torg with - | Some (Cil.TInt (ikorg, _)) when p ikorg -> - if M.tracing then M.trace "cong-cast" "some case"; - x - | _ -> top () - - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov (t : Cil.ikind) x = - let pretty_bool _ x = Pretty.text (string_of_bool x) in - let res = cast_to ?torg ?no_ov t x in - if M.tracing then M.trace "cong-cast" "Cast %a to %a (no_ov: %a) = %a" pretty x Cil.d_ikind t (Pretty.docOpt (pretty_bool ())) no_ov pretty res; - res - - let widen = join - - let widen ik x y = - let res = widen ik x y in - if M.tracing then M.trace "congruence" "widen %a %a -> %a" pretty x pretty y pretty res; - res - - let narrow = meet - - let log f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_bool i1, to_bool i2 with - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - let c_logor = log (||) - let c_logand = log (&&) - - let log1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_bool i1 with - | Some x -> of_bool ik (f ik x) - | _ -> top_of ik - - let c_lognot = log1 (fun _ik -> not) - - let shift_right _ _ _ = top() - - let shift_right ik x y = - let res = shift_right ik x y in - if M.tracing then M.trace "congruence" "shift_right : %a %a becomes %a " pretty x pretty y pretty res; - res - - let shift_left ik x y = - (* Naive primality test *) - (* let is_prime n = - let n = Z.abs n in - let rec is_prime' d = - (d *: d >: n) || ((not ((n %: d) =: Z.zero)) && (is_prime' [@tailcall]) (d +: Z.one)) - in - not (n =: Z.one) && is_prime' (Z.of_int 2) - in *) - match x, y with - | None, None -> None - | None, _ - | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c, m), Some (c', m') when Cil.isSigned ik || c <: Z.zero || c' <: Z.zero -> top_of ik - | Some (c, m), Some (c', m') -> - let (_, max_ik) = range ik in - if m =: Z.zero && m' =: Z.zero then - normalize ik @@ Some (Z.logand max_ik (Z.shift_left c (Z.to_int c')), Z.zero) - else - let x = Z.logand max_ik (Z.shift_left Z.one (Z.to_int c')) in (* 2^c' *) - (* TODO: commented out because fails test with _Bool *) - (* if is_prime (m' +: Z.one) then - normalize ik @@ Some (x *: c, Z.gcd (x *: m) ((c *: x) *: (m' +: Z.one))) - else *) - normalize ik @@ Some (x *: c, Z.gcd (x *: m) (c *: x)) - - let shift_left ik x y = - let res = shift_left ik x y in - if M.tracing then M.trace "congruence" "shift_left : %a %a becomes %a " pretty x pretty y pretty res; - res - - (* Handle unsigned overflows. - From n === k mod (2^a * b), we conclude n === k mod 2^a, for a <= bitwidth. - The congruence modulo b may not persist on an overflow. *) - let handle_overflow ik (c, m) = - if m =: Z.zero then - normalize ik (Some (c, m)) - else - (* Find largest m'=2^k (for some k) such that m is divisible by m' *) - let tz = Z.trailing_zeros m in - let m' = Z.shift_left Z.one tz in - - let max = (snd (Size.range ik)) +: Z.one in - if m' >=: max then - (* if m' >= 2 ^ {bitlength}, there is only one value in range *) - let c' = c %: max in - Some (c', Z.zero) - else - normalize ik (Some (c, m')) - - let mul ?(no_ov=false) ik x y = - let no_ov_case (c1, m1) (c2, m2) = - c1 *: c2, Z.gcd (c1 *: m2) (Z.gcd (m1 *: c2) (m1 *: m2)) - in - match x, y with - | None, None -> bot () - | None, _ | _, None -> - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c1, m1), Some (c2, m2) when no_ov -> - Some (no_ov_case (c1, m1) (c2, m2)) - | Some (c1, m1), Some (c2, m2) when m1 =: Z.zero && m2 =: Z.zero && not (Cil.isSigned ik) -> - let (_, max_ik) = range ik in - Some ((c1 *: c2) %: (max_ik +: Z.one), Z.zero) - | Some a, Some b when not (Cil.isSigned ik) -> - handle_overflow ik (no_ov_case a b ) - | _ -> top () - - let mul ?no_ov ik x y = - let res = mul ?no_ov ik x y in - if M.tracing then M.trace "congruence" "mul : %a %a -> %a " pretty x pretty y pretty res; - res - - let neg ?(no_ov=false) ik x = - match x with - | None -> bot() - | Some _ -> mul ~no_ov ik (of_int ik (Z.of_int (-1))) x - - let add ?(no_ov=false) ik x y = - let no_ov_case (c1, m1) (c2, m2) = - c1 +: c2, Z.gcd m1 m2 - in - match (x, y) with - | None, None -> bot () - | None, _ | _, None -> - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some a, Some b when no_ov -> - normalize ik (Some (no_ov_case a b)) - | Some (c1, m1), Some (c2, m2) when m1 =: Z.zero && m2 =: Z.zero && not (Cil.isSigned ik) -> - let (_, max_ik) = range ik in - Some((c1 +: c2) %: (max_ik +: Z.one), Z.zero) - | Some a, Some b when not (Cil.isSigned ik) -> - handle_overflow ik (no_ov_case a b) - | _ -> top () - - - let add ?no_ov ik x y = - let res = add ?no_ov ik x y in - if M.tracing then - M.trace "congruence" "add : %a %a -> %a" pretty x pretty y - pretty res ; - res - - let sub ?(no_ov=false) ik x y = add ~no_ov ik x (neg ~no_ov ik y) - - - let sub ?no_ov ik x y = - let res = sub ?no_ov ik x y in - if M.tracing then - M.trace "congruence" "sub : %a %a -> %a" pretty x pretty y - pretty res ; - res - - let lognot ik x = match x with - | None -> None - | Some (c, m) -> - if (Cil.isSigned ik) then - sub ik (neg ik x) one - else - let (_, max_ik) = range ik in - Some (Z.sub max_ik c, m) - - (** The implementation of the bit operations could be improved based on the master’s thesis - 'Abstract Interpretation and Abstract Domains' written by Stefan Bygde. - see: http://www.es.mdh.se/pdf_publications/948.pdf *) - let bit2 f ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c, m), Some (c', m') -> - if m =: Z.zero && m' =: Z.zero then Some (f c c', Z.zero) - else top () - - let logor ik x y = bit2 Z.logor ik x y - - let logand ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c, m), Some (c', m') -> - if m =: Z.zero && m' =: Z.zero then - (* both arguments constant *) - Some (Z.logand c c', Z.zero) - else if m' =: Z.zero && c' =: Z.one && Z.rem m (Z.of_int 2) =: Z.zero then - (* x & 1 and x == c (mod 2*z) *) - (* Value is equal to LSB of c *) - Some (Z.logand c c', Z.zero) - else - top () - - let logxor ik x y = bit2 Z.logxor ik x y - - let rem ik x y = - match x, y with - | None, None -> bot() - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c1, m1), Some(c2, m2) -> - if m2 =: Z.zero then - if (c2 |: m1) && (c1 %: c2 =: Z.zero || m1 =: Z.zero || not (Cil.isSigned ik)) then - Some (c1 %: c2, Z.zero) - else - normalize ik (Some (c1, (Z.gcd m1 c2))) - else - normalize ik (Some (c1, Z.gcd m1 (Z.gcd c2 m2))) - - let rem ik x y = let res = rem ik x y in - if M.tracing then M.trace "congruence" "rem : %a %a -> %a " pretty x pretty y pretty res; - res - - let div ?(no_ov=false) ik x y = - match x,y with - | None, None -> bot () - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, x when leq zero x -> top () - | Some(c1, m1), Some(c2, m2) when not no_ov && m2 =: Z.zero && c2 =: Z.neg Z.one -> top () - | Some(c1, m1), Some(c2, m2) when m1 =: Z.zero && m2 =: Z.zero -> Some (c1 /: c2, Z.zero) - | Some(c1, m1), Some(c2, m2) when m2 =: Z.zero && c2 |: m1 && c2 |: c1 -> Some (c1 /: c2, m1 /: c2) - | _, _ -> top () - - - let div ?no_ov ik x y = - let res = div ?no_ov ik x y in - if M.tracing then - M.trace "congruence" "div : %a %a -> %a" pretty x pretty y pretty - res ; - res - - let ne ik (x: t) (y: t) = match x, y with - | Some (c1, m1), Some (c2, m2) when (m1 =: Z.zero) && (m2 =: Z.zero) -> of_bool ik (not (c1 =: c2 )) - | x, y -> if meet ik x y = None then of_bool ik true else top_bool - - let eq ik (x: t) (y: t) = match x, y with - | Some (c1, m1), Some (c2, m2) when (m1 =: Z.zero) && (m2 =: Z.zero) -> of_bool ik (c1 =: c2) - | x, y -> if meet ik x y <> None then top_bool else of_bool ik false - - let comparison ik op x y = match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (c1, m1), Some (c2, m2) -> - if m1 =: Z.zero && m2 =: Z.zero then - if op c1 c2 then of_bool ik true else of_bool ik false - else - top_bool - - let ge ik x y = comparison ik (>=:) x y - - let ge ik x y = - let res = ge ik x y in - if M.tracing then M.trace "congruence" "greater or equal : %a %a -> %a " pretty x pretty y pretty res; - res - - let le ik x y = comparison ik (<=:) x y - - let le ik x y = - let res = le ik x y in - if M.tracing then M.trace "congruence" "less or equal : %a %a -> %a " pretty x pretty y pretty res; - res - - let gt ik x y = comparison ik (>:) x y - - - let gt ik x y = - let res = gt ik x y in - if M.tracing then M.trace "congruence" "greater than : %a %a -> %a " pretty x pretty y pretty res; - res - - let lt ik x y = comparison ik (<:) x y - - let lt ik x y = - let res = lt ik x y in - if M.tracing then M.trace "congruence" "less than : %a %a -> %a " pretty x pretty y pretty res; - res - - let invariant_ikind e ik x = - match x with - | x when is_top x -> Invariant.top () - | Some (c, m) when m =: Z.zero -> - IntInvariant.of_int e ik c - | Some (c, m) -> - let open Cil in - let (c, m) = BatTuple.Tuple2.mapn (fun a -> kintegerCilint ik a) (c, m) in - Invariant.of_exp (BinOp (Eq, (BinOp (Mod, e, m, TInt(ik,[]))), c, intType)) - | None -> Invariant.none - - let arbitrary ik = - let open QCheck in - let int_arb = map ~rev:Z.to_int64 Z.of_int64 GobQCheck.Arbitrary.int64 in - let cong_arb = pair int_arb int_arb in - let of_pair ik p = normalize ik (Some p) in - let to_pair = Option.get in - set_print show (map ~rev:to_pair (of_pair ik) cong_arb) - - let refine_with_interval ik (cong : t) (intv : (int_t * int_t ) option) : t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if m =: Z.zero then - if c <: x || c >: y then None else Some (c, Z.zero) - else - let rcx = x +: ((c -: x) %: Z.abs m) in - let lcy = y -: ((y -: c) %: Z.abs m) in - if rcx >: lcy then None - else if rcx =: lcy then Some (rcx, Z.zero) - else cong - | _ -> None - - let refine_with_interval ik (cong : t) (intv : (int_t * int_t) option) : t = - let pretty_intv _ i = - match i with - | Some (l, u) -> Pretty.dprintf "[%a,%a]" GobZ.pretty l GobZ.pretty u - | _ -> Pretty.text ("Display Error") in - let refn = refine_with_interval ik cong intv in - if M.tracing then M.trace "refine" "cong_refine_with_interval %a %a -> %a" pretty cong pretty_intv intv pretty refn; - refn - - let refine_with_congruence ik a b = meet ik a b - let refine_with_excl_list ik a b = a - let refine_with_incl_list ik a b = a - - let project ik p t = t -end - module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct include D From f5c1a1cf049a983f15384568a16dd55625340ec8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 16:59:38 +0300 Subject: [PATCH 464/566] Rename IntDomain0 -> EnumsDomain for split --- src/cdomain/value/cdomains/{intDomain0.ml => int/enumsDomain.ml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/cdomain/value/cdomains/{intDomain0.ml => int/enumsDomain.ml} (100%) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/int/enumsDomain.ml similarity index 100% rename from src/cdomain/value/cdomains/intDomain0.ml rename to src/cdomain/value/cdomains/int/enumsDomain.ml From 92ec19e4007986f1d48b2da79d42456cda83fe95 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:00:03 +0300 Subject: [PATCH 465/566] Remove non-EnumsDomain parts --- src/cdomain/value/cdomains/int/enumsDomain.ml | 2409 +---------------- 1 file changed, 1 insertion(+), 2408 deletions(-) diff --git a/src/cdomain/value/cdomains/int/enumsDomain.ml b/src/cdomain/value/cdomains/int/enumsDomain.ml index 8dd8b07f74..d0208feeff 100644 --- a/src/cdomain/value/cdomains/int/enumsDomain.ml +++ b/src/cdomain/value/cdomains/int/enumsDomain.ml @@ -1,2380 +1,5 @@ -open GobConfig -open GoblintCil -open Pretty -open PrecisionUtil +open IntDomain0 -module M = Messages - -let (%) = Batteries.(%) -let (|?) = Batteries.(|?) - -exception IncompatibleIKinds of string -exception Unknown -exception Error -exception ArithmeticOnIntegerBot of string - - - - -(** Define records that hold mutable variables representing different Configuration values. - * These values are used to keep track of whether or not the corresponding Config values are en-/disabled *) -type ana_int_config_values = { - mutable interval_threshold_widening : bool option; - mutable interval_narrow_by_meet : bool option; - mutable def_exc_widen_by_join : bool option; - mutable interval_threshold_widening_constants : string option; - mutable refinement : string option; -} - -let ana_int_config: ana_int_config_values = { - interval_threshold_widening = None; - interval_narrow_by_meet = None; - def_exc_widen_by_join = None; - interval_threshold_widening_constants = None; - refinement = None; -} - -let get_interval_threshold_widening () = - if ana_int_config.interval_threshold_widening = None then - ana_int_config.interval_threshold_widening <- Some (get_bool "ana.int.interval_threshold_widening"); - Option.get ana_int_config.interval_threshold_widening - -let get_interval_narrow_by_meet () = - if ana_int_config.interval_narrow_by_meet = None then - ana_int_config.interval_narrow_by_meet <- Some (get_bool "ana.int.interval_narrow_by_meet"); - Option.get ana_int_config.interval_narrow_by_meet - -let get_def_exc_widen_by_join () = - if ana_int_config.def_exc_widen_by_join = None then - ana_int_config.def_exc_widen_by_join <- Some (get_bool "ana.int.def_exc_widen_by_join"); - Option.get ana_int_config.def_exc_widen_by_join - -let get_interval_threshold_widening_constants () = - if ana_int_config.interval_threshold_widening_constants = None then - ana_int_config.interval_threshold_widening_constants <- Some (get_string "ana.int.interval_threshold_widening_constants"); - Option.get ana_int_config.interval_threshold_widening_constants - -let get_refinement () = - if ana_int_config.refinement = None then - ana_int_config.refinement <- Some (get_string "ana.int.refinement"); - Option.get ana_int_config.refinement - - - -(** Whether for a given ikind, we should compute with wrap-around arithmetic. - * Always for unsigned types, for signed types if 'sem.int.signed_overflow' is 'assume_wraparound' *) -let should_wrap ik = not (Cil.isSigned ik) || get_string "sem.int.signed_overflow" = "assume_wraparound" - -(** Whether for a given ikind, we should assume there are no overflows. - * Always false for unsigned types, true for signed types if 'sem.int.signed_overflow' is 'assume_none' *) -let should_ignore_overflow ik = Cil.isSigned ik && get_string "sem.int.signed_overflow" = "assume_none" - -let widening_thresholds = ResettableLazy.from_fun WideningThresholds.thresholds -let widening_thresholds_desc = ResettableLazy.from_fun (List.rev % WideningThresholds.thresholds) - -type overflow_info = { overflow: bool; underflow: bool;} - -let set_overflow_flag ~cast ~underflow ~overflow ik = - if !AnalysisState.executing_speculative_computations then - (* Do not produce warnings when the operations are not actually happening in code *) - () - else - let signed = Cil.isSigned ik in - if !AnalysisState.postsolving && signed && not cast then - AnalysisState.svcomp_may_overflow := true; - let sign = if signed then "Signed" else "Unsigned" in - match underflow, overflow with - | true, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190; CWE 191] "%s integer overflow and underflow" sign - | true, false -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 191] "%s integer underflow" sign - | false, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190] "%s integer overflow" sign - | false, false -> assert false - -let reset_lazy () = - ResettableLazy.reset widening_thresholds; - ResettableLazy.reset widening_thresholds_desc; - ana_int_config.interval_threshold_widening <- None; - ana_int_config.interval_narrow_by_meet <- None; - ana_int_config.def_exc_widen_by_join <- None; - ana_int_config.interval_threshold_widening_constants <- None; - ana_int_config.refinement <- None - -module type Arith = -sig - type t - val neg: t -> t - val add: t -> t -> t - val sub: t -> t -> t - val mul: t -> t -> t - val div: t -> t -> t - val rem: t -> t -> t - - val lt: t -> t -> t - val gt: t -> t -> t - val le: t -> t -> t - val ge: t -> t -> t - val eq: t -> t -> t - val ne: t -> t -> t - - val lognot: t -> t - val logand: t -> t -> t - val logor : t -> t -> t - val logxor: t -> t -> t - - val shift_left : t -> t -> t - val shift_right: t -> t -> t - - val c_lognot: t -> t - val c_logand: t -> t -> t - val c_logor : t -> t -> t - -end - -module type ArithIkind = -sig - type t - val neg: Cil.ikind -> t -> t - val add: Cil.ikind -> t -> t -> t - val sub: Cil.ikind -> t -> t -> t - val mul: Cil.ikind -> t -> t -> t - val div: Cil.ikind -> t -> t -> t - val rem: Cil.ikind -> t -> t -> t - - val lt: Cil.ikind -> t -> t -> t - val gt: Cil.ikind -> t -> t -> t - val le: Cil.ikind -> t -> t -> t - val ge: Cil.ikind -> t -> t -> t - val eq: Cil.ikind -> t -> t -> t - val ne: Cil.ikind -> t -> t -> t - - val lognot: Cil.ikind -> t -> t - val logand: Cil.ikind -> t -> t -> t - val logor : Cil.ikind -> t -> t -> t - val logxor: Cil.ikind -> t -> t -> t - - val shift_left : Cil.ikind -> t -> t -> t - val shift_right: Cil.ikind -> t -> t -> t - - val c_lognot: Cil.ikind -> t -> t - val c_logand: Cil.ikind -> t -> t -> t - val c_logor : Cil.ikind -> t -> t -> t - -end - -(* Shared functions between S and Z *) -module type B = -sig - include Lattice.S - type int_t - val bot_of: Cil.ikind -> t - val top_of: Cil.ikind -> t - val to_int: t -> int_t option - val equal_to: int_t -> t -> [`Eq | `Neq | `Top] - - val to_bool: t -> bool option - val to_excl_list: t -> (int_t list * (int64 * int64)) option - val of_excl_list: Cil.ikind -> int_t list -> t - val is_excl_list: t -> bool - - val to_incl_list: t -> int_t list option - - val maximal : t -> int_t option - val minimal : t -> int_t option - - val cast_to: ?suppress_ovwarn:bool -> ?torg:Cil.typ -> Cil.ikind -> t -> t -end - -(** Interface of IntDomain implementations that do not take ikinds for arithmetic operations yet. TODO: Should be ported to S in the future. *) -module type IkindUnawareS = -sig - include B - include Arith with type t := t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: int_t -> t - val of_bool: bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val arbitrary: unit -> t QCheck.arbitrary - val invariant: Cil.exp -> t -> Invariant.t -end - -(** Interface of IntDomain implementations taking an ikind for arithmetic operations *) -module type S = -sig - include B - include ArithIkind with type t:= t - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val neg : ?no_ov:bool -> Cil.ikind -> t -> t - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t - - val join: Cil.ikind -> t -> t -> t - val meet: Cil.ikind -> t -> t -> t - val narrow: Cil.ikind -> t -> t -> t - val widen: Cil.ikind -> t -> t -> t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val is_top_of: Cil.ikind -> t -> bool - val invariant_ikind : Cil.exp -> Cil.ikind -> t -> Invariant.t - - val refine_with_congruence: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_interval: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_excl_list: Cil.ikind -> t -> (int_t list * (int64 * int64)) option -> t - val refine_with_incl_list: Cil.ikind -> t -> int_t list option -> t - - val project: Cil.ikind -> int_precision -> t -> t - val arbitrary: Cil.ikind -> t QCheck.arbitrary -end - -module type SOverflow = -sig - - include S - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val neg : ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val of_int : Cil.ikind -> int_t -> t * overflow_info - - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t * overflow_info - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - - val shift_left : Cil.ikind -> t -> t -> t * overflow_info - - val shift_right : Cil.ikind -> t -> t -> t * overflow_info -end - -module type Y = -sig - (* include B *) - include B - include Arith with type t:= t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val is_top_of: Cil.ikind -> t -> bool - - val project: int_precision -> t -> t - val invariant: Cil.exp -> t -> Invariant.t -end - -module type Z = Y with type int_t = Z.t - - -module IntDomLifter (I : S) = -struct - open Cil - 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 (GobPretty.sprintf "ikinds %a and %a are incompatible. Values: %a and %a" CilType.Ikind.pretty x.ikind CilType.Ikind.pretty y.ikind I.pretty x.v I.pretty y.v)) - let lift op x = {x with v = op x.ikind x.v } - (* For logical operations the result is of type int *) - let lift_logical op x = {v = op x.ikind x.v; ikind = Cil.IInt} - let lift2 op x y = check_ikinds x y; {x with v = op x.ikind x.v y.v } - let lift2_cmp op x y = check_ikinds x y; {v = op x.ikind x.v y.v; ikind = Cil.IInt} - - let bot_of ikind = { v = I.bot_of ikind; ikind} - let bot () = failwith "bot () is not implemented for IntDomLifter." - let is_bot x = I.is_bot x.v - let top_of ikind = { v = I.top_of ikind; ikind} - let top () = failwith "top () is not implemented for IntDomLifter." - let is_top x = I.is_top x.v - - (* Leq does not check for ikind, because it is used in invariant with arguments of different type. - TODO: check ikinds here and fix invariant to work with right ikinds *) - let leq x y = I.leq x.v y.v - let join = lift2 I.join - let meet = lift2 I.meet - let widen = lift2 I.widen - let narrow = lift2 I.narrow - - let show x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - "⊤" - else - I.show x.v (* TODO add ikind to output *) - let pretty () x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - Pretty.text "⊤" - else - I.pretty () x.v (* TODO add ikind to output *) - let pretty_diff () (x, y) = I.pretty_diff () (x.v, y.v) (* TODO check ikinds, add them to output *) - let printXml o x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - BatPrintf.fprintf o "\n\n⊤\n\n\n" - else - I.printXml o x.v (* TODO add ikind to output *) - (* This is for debugging *) - let name () = "IntDomLifter(" ^ (I.name ()) ^ ")" - let to_yojson x = I.to_yojson x.v - let invariant e x = - let e' = Cilfacade.mkCast ~e ~newt:(TInt (x.ikind, [])) in - I.invariant_ikind e' x.ikind x.v - let tag x = I.tag x.v - let arbitrary ik = failwith @@ "Arbitrary not implement for " ^ (name ()) ^ "." - let to_int x = I.to_int x.v - let of_int ikind x = { v = I.of_int ikind x; ikind} - let equal_to i x = I.equal_to i x.v - let to_bool x = I.to_bool x.v - let of_bool ikind b = { v = I.of_bool ikind b; ikind} - let to_excl_list x = I.to_excl_list x.v - let of_excl_list ikind is = {v = I.of_excl_list ikind is; ikind} - let is_excl_list x = I.is_excl_list x.v - let to_incl_list x = I.to_incl_list x.v - let of_interval ?(suppress_ovwarn=false) ikind (lb,ub) = {v = I.of_interval ~suppress_ovwarn ikind (lb,ub); ikind} - let of_congruence ikind (c,m) = {v = I.of_congruence ikind (c,m); ikind} - let starting ?(suppress_ovwarn=false) ikind i = {v = I.starting ~suppress_ovwarn ikind i; ikind} - let ending ?(suppress_ovwarn=false) ikind i = {v = I.ending ~suppress_ovwarn ikind i; ikind} - let maximal x = I.maximal x.v - let minimal x = I.minimal x.v - - let neg = lift I.neg - let add = lift2 I.add - let sub = lift2 I.sub - let mul = lift2 I.mul - let div = lift2 I.div - let rem = lift2 I.rem - let lt = lift2_cmp I.lt - let gt = lift2_cmp I.gt - let le = lift2_cmp I.le - let ge = lift2_cmp I.ge - let eq = lift2_cmp I.eq - let ne = lift2_cmp I.ne - let lognot = lift I.lognot - let logand = lift2 I.logand - let logor = lift2 I.logor - let logxor = lift2 I.logxor - let shift_left x y = {x with v = I.shift_left x.ikind x.v y.v } (* TODO check ikinds*) - let shift_right x y = {x with v = I.shift_right x.ikind x.v y.v } (* TODO check ikinds*) - let c_lognot = lift_logical I.c_lognot - let c_logand = lift2 I.c_logand - let c_logor = lift2 I.c_logor - - let cast_to ?(suppress_ovwarn=false) ?torg ikind x = {v = I.cast_to ~suppress_ovwarn ~torg:(TInt(x.ikind,[])) ikind x.v; ikind} - - let is_top_of ik x = ik = x.ikind && I.is_top_of ik x.v - - let relift x = { v = I.relift x.v; ikind = x.ikind } - - let project p v = { v = I.project v.ikind p v.v; ikind = v.ikind } -end - -module type Ikind = -sig - val ikind: unit -> Cil.ikind -end - -module PtrDiffIkind : Ikind = -struct - let ikind = Cilfacade.ptrdiff_ikind -end - -module IntDomWithDefaultIkind (I: Y) (Ik: Ikind) : Y with type t = I.t and type int_t = I.int_t = -struct - include I - let top () = I.top_of (Ik.ikind ()) - let bot () = I.bot_of (Ik.ikind ()) -end - -module Size = struct (* size in bits as int, range as int64 *) - open Cil - let sign x = if Z.compare x Z.zero < 0 then `Signed else `Unsigned - - let top_typ = TInt (ILongLong, []) - let min_for x = intKindForValue x (sign x = `Unsigned) - let bit = function (* bits needed for representation *) - | IBool -> 1 - | ik -> bytesSizeOfInt ik * 8 - let is_int64_big_int x = Z.fits_int64 x - let card ik = (* cardinality *) - let b = bit ik in - Z.shift_left Z.one b - let bits ik = (* highest bits for neg/pos values *) - let s = bit ik in - if isSigned ik then s-1, s-1 else 0, s - let bits_i64 ik = BatTuple.Tuple2.mapn Int64.of_int (bits ik) - let range ik = - let a,b = bits ik in - let x = if isSigned ik then Z.neg (Z.shift_left Z.one a) (* -2^a *) else Z.zero in - let y = Z.pred (Z.shift_left Z.one b) in (* 2^b - 1 *) - x,y - - let is_cast_injective ~from_type ~to_type = - let (from_min, from_max) = range (Cilfacade.get_ikind from_type) in - let (to_min, to_max) = range (Cilfacade.get_ikind to_type) in - if M.tracing then M.trace "int" "is_cast_injective %a (%a, %a) -> %a (%a, %a)" CilType.Typ.pretty from_type GobZ.pretty from_min GobZ.pretty from_max CilType.Typ.pretty to_type GobZ.pretty to_min GobZ.pretty to_max; - Z.compare to_min from_min <= 0 && Z.compare from_max to_max <= 0 - - let cast t x = (* TODO: overflow is implementation-dependent! *) - if t = IBool then - (* C11 6.3.1.2 Boolean type *) - if Z.equal x Z.zero then Z.zero else Z.one - else - let a,b = range t in - let c = card t in - let y = Z.erem x c in - let y = if Z.gt y b then Z.sub y c - else if Z.lt y a then Z.add y c - else y - in - if M.tracing then M.tracel "cast" "Cast %a to range [%a, %a] (%a) = %a (%s in int64)" GobZ.pretty x GobZ.pretty a GobZ.pretty b GobZ.pretty c GobZ.pretty y (if is_int64_big_int y then "fits" else "does not fit"); - y - - let min_range_sign_agnostic x = - let size ik = - let a,b = bits_i64 ik in - Int64.neg a,b - in - if sign x = `Signed then - size (min_for x) - else - let a, b = size (min_for x) in - if b <= 64L then - let upper_bound_less = Int64.sub b 1L in - let max_one_less = Z.(pred @@ shift_left Z.one (Int64.to_int upper_bound_less)) in - if x <= max_one_less then - a, upper_bound_less - else - a,b - else - a, b - - (* From the number of bits used to represent a positive value, determines the maximal representable value *) - let max_from_bit_range pos_bits = Z.(pred @@ shift_left Z.one (to_int (Z.of_int64 pos_bits))) - - (* From the number of bits used to represent a non-positive value, determines the minimal representable value *) - let min_from_bit_range neg_bits = Z.(if neg_bits = 0L then Z.zero else neg @@ shift_left Z.one (to_int (neg (Z.of_int64 neg_bits)))) - -end - - -module StdTop (B: sig type t val top_of: Cil.ikind -> t end) = struct - open B - (* these should be overwritten for better precision if possible: *) - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ik x = top_of ik - let ending ?(suppress_ovwarn=false) ik x = top_of ik - let maximal x = None - let minimal x = None -end - -module Std (B: sig - type t - val name: unit -> string - val top_of: Cil.ikind -> t - val bot_of: Cil.ikind -> t - val show: t -> string - val equal: t -> t -> bool - end) = struct - include Printable.StdLeaf - let name = B.name (* overwrite the one from Printable.Std *) - open B - let is_top x = failwith "is_top not implemented for IntDomain.Std" - let is_bot x = B.equal x (bot_of Cil.IInt) (* Here we assume that the representation of bottom is independent of the ikind - This may be true for intdomain implementations, but not e.g. for IntDomLifter. *) - let is_top_of ik x = B.equal x (top_of ik) - - (* all output is based on B.show *) - include Printable.SimpleShow ( - struct - type nonrec t = t - let show = show - end - ) - let pretty_diff () (x,y) = dprintf "%s: %a instead of %a" (name ()) pretty x pretty y - - include StdTop (B) -end - -(* Textbook interval arithmetic, without any overflow handling etc. *) -module IntervalArith (Ints_t : IntOps.IntOps) = struct - let min4 a b c d = Ints_t.min (Ints_t.min a b) (Ints_t.min c d) - let max4 a b c d = Ints_t.max (Ints_t.max a b) (Ints_t.max c d) - - let mul (x1, x2) (y1, y2) = - let x1y1 = (Ints_t.mul x1 y1) in - let x1y2 = (Ints_t.mul x1 y2) in - let x2y1 = (Ints_t.mul x2 y1) in - let x2y2 = (Ints_t.mul x2 y2) in - (min4 x1y1 x1y2 x2y1 x2y2, max4 x1y1 x1y2 x2y1 x2y2) - - let shift_left (x1,x2) (y1,y2) = - let y1p = Ints_t.shift_left Ints_t.one y1 in - let y2p = Ints_t.shift_left Ints_t.one y2 in - mul (x1, x2) (y1p, y2p) - - let div (x1, x2) (y1, y2) = - let x1y1n = (Ints_t.div x1 y1) in - let x1y2n = (Ints_t.div x1 y2) in - let x2y1n = (Ints_t.div x2 y1) in - let x2y2n = (Ints_t.div x2 y2) in - let x1y1p = (Ints_t.div x1 y1) in - let x1y2p = (Ints_t.div x1 y2) in - let x2y1p = (Ints_t.div x2 y1) in - let x2y2p = (Ints_t.div x2 y2) in - (min4 x1y1n x1y2n x2y1n x2y2n, max4 x1y1p x1y2p x2y1p x2y2p) - - let add (x1, x2) (y1, y2) = (Ints_t.add x1 y1, Ints_t.add x2 y2) - let sub (x1, x2) (y1, y2) = (Ints_t.sub x1 y2, Ints_t.sub x2 y1) - - let neg (x1, x2) = (Ints_t.neg x2, Ints_t.neg x1) - - let one = (Ints_t.one, Ints_t.one) - let zero = (Ints_t.zero, Ints_t.zero) - let top_bool = (Ints_t.zero, Ints_t.one) - - let to_int (x1, x2) = - if Ints_t.equal x1 x2 then Some x1 else None - - let upper_threshold u max_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - let max_ik' = Ints_t.to_bigint max_ik in - let t = List.find_opt (fun x -> Z.compare u x <= 0 && Z.compare x max_ik' <= 0) ts in - BatOption.map_default Ints_t.of_bigint max_ik t - let lower_threshold l min_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - let min_ik' = Ints_t.to_bigint min_ik in - let t = List.find_opt (fun x -> Z.compare l x >= 0 && Z.compare x min_ik' >= 0) ts in - BatOption.map_default Ints_t.of_bigint min_ik t - let is_upper_threshold u = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - List.exists (Z.equal u) ts - let is_lower_threshold l = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - List.exists (Z.equal l) ts -end - -module IntInvariant = -struct - let of_int e ik x = - if get_bool "witness.invariant.exact" then - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) - else - Invariant.none - - let of_incl_list e ik ps = - match ps with - | [_; _] when ik = IBool && not (get_bool "witness.invariant.inexact-type-bounds") -> - assert (List.mem Z.zero ps); - assert (List.mem Z.one ps); - Invariant.none - | [_] when get_bool "witness.invariant.exact" -> - Invariant.none - | _ :: _ :: _ - | [_] | [] -> - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in - Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) (Invariant.bot ()) ps - - let of_interval_opt e ik = function - | (Some x1, Some x2) when Z.equal x1 x2 -> - of_int e ik x1 - | x1_opt, x2_opt -> - let (min_ik, max_ik) = Size.range ik in - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in - let i1 = - match x1_opt, inexact_type_bounds with - | Some x1, false when Z.equal min_ik x1 -> Invariant.none - | Some x1, _ -> Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1, e, intType)) - | None, _ -> Invariant.none - in - let i2 = - match x2_opt, inexact_type_bounds with - | Some x2, false when Z.equal x2 max_ik -> Invariant.none - | Some x2, _ -> Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) - | None, _ -> Invariant.none - in - Invariant.(i1 && i2) - - let of_interval e ik (x1, x2) = - of_interval_opt e ik (Some x1, Some x2) - - let of_excl_list e ik ns = - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in - Invariant.(a && i) - ) (Invariant.top ()) ns -end - -module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = -struct - let name () = "intervals" - type int_t = Ints_t.t - type t = (Ints_t.t * Ints_t.t) option [@@deriving eq, ord, hash] - module IArith = IntervalArith (Ints_t) - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - let top_of ik = Some (range ik) - let bot () = None - let bot_of ik = bot () (* TODO: improve *) - - let show = function None -> "bottom" | Some (x,y) -> "["^Ints_t.to_string x^","^Ints_t.to_string y^"]" - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let equal_to i = function - | None -> failwith "unsupported: equal_to with bottom" - | Some (a, b) -> - if a = b && b = i then `Eq else if Ints_t.compare a i <= 0 && Ints_t.compare i b <=0 then `Top else `Neq - - let norm ?(suppress_ovwarn=false) ?(cast=false) ik : (t -> t * overflow_info) = function None -> (None, {underflow=false; overflow=false}) | Some (x,y) -> - if Ints_t.compare x y > 0 then - (None,{underflow=false; overflow=false}) - else ( - let (min_ik, max_ik) = range ik in - let underflow = Ints_t.compare min_ik x > 0 in - let overflow = Ints_t.compare max_ik y < 0 in - let ov_info = { underflow = underflow && not suppress_ovwarn; overflow = overflow && not suppress_ovwarn } in - let v = - if underflow || overflow then - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (Ints_t.sub max_ik min_ik) in - let resdiff = Ints_t.abs (Ints_t.sub y x) in - if Ints_t.compare resdiff diff > 0 then - top_of ik - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if Ints_t.compare l u <= 0 then - Some (l, u) - else - (* Interval that wraps around (begins to the right of its end). We can not represent such intervals *) - top_of ik - else if not cast && should_ignore_overflow ik then - let tl, tu = BatOption.get @@ top_of ik in - Some (Ints_t.max tl x, Ints_t.min tu y) - else - top_of ik - else - Some (x,y) - in - (v, ov_info) - ) - - let leq (x:t) (y:t) = - match x, y with - | None, _ -> true - | Some _, None -> false - | Some (x1,x2), Some (y1,y2) -> Ints_t.compare x1 y1 >= 0 && Ints_t.compare x2 y2 <= 0 - - let join ik (x:t) y = - match x, y with - | None, z | z, None -> z - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.min x1 y1, Ints_t.max x2 y2) |> fst - - let meet ik (x:t) y = - match x, y with - | None, z | z, None -> None - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.max x1 y1, Ints_t.min x2 y2) |> fst - - (* TODO: change to_int signature so it returns a big_int *) - let to_int x = Option.bind x (IArith.to_int) - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm ~suppress_ovwarn ik @@ Some (x,y) - let of_int ik (x: int_t) = of_interval ik (x,x) - let zero = Some IArith.zero - let one = Some IArith.one - let top_bool = Some IArith.top_bool - - let of_bool _ik = function true -> one | false -> zero - let to_bool (a: t) = match a with - | None -> None - | Some (l, u) when Ints_t.compare l Ints_t.zero = 0 && Ints_t.compare u Ints_t.zero = 0 -> Some false - | x -> if leq zero x then None else Some true - - let starting ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (fst (range ik), n) - - (* TODO: change signature of maximal, minimal to return big_int*) - let maximal = function None -> None | Some (x,y) -> Some y - let minimal = function None -> None | Some (x,y) -> Some x - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov t = norm ~cast:true t (* norm does all overflow handling *) - - let widen ik x y = - match x, y with - | None, z | z, None -> z - | Some (l0,u0), Some (l1,u1) -> - let (min_ik, max_ik) = range ik in - let threshold = get_interval_threshold_widening () in - let l2 = - if Ints_t.compare l0 l1 = 0 then l0 - else if threshold then IArith.lower_threshold l1 min_ik - else min_ik - in - let u2 = - if Ints_t.compare u0 u1 = 0 then u0 - else if threshold then IArith.upper_threshold u1 max_ik - else max_ik - in - norm ik @@ Some (l2,u2) |> fst - let widen ik x y = - let r = widen ik x y in - if M.tracing && not (equal x y) then M.tracel "int" "interval widen %a %a -> %a" pretty x pretty y pretty r; - assert (leq x y); (* TODO: remove for performance reasons? *) - r - - let narrow ik x y = - match x, y with - | _,None | None, _ -> None - | Some (x1,x2), Some (y1,y2) -> - let threshold = get_interval_threshold_widening () in - let (min_ik, max_ik) = range ik in - let lr = if Ints_t.compare min_ik x1 = 0 || threshold && Ints_t.compare y1 x1 > 0 && IArith.is_lower_threshold x1 then y1 else x1 in - let ur = if Ints_t.compare max_ik x2 = 0 || threshold && Ints_t.compare y2 x2 < 0 && IArith.is_upper_threshold x2 then y2 else x2 in - norm ik @@ Some (lr,ur) |> fst - - - let narrow ik x y = - if get_interval_narrow_by_meet () then - meet ik x y - else - narrow ik x y - - let log f ~annihilator ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_bool i1, to_bool i2 with - | Some x, _ when x = annihilator -> of_bool ik annihilator - | _, Some y when y = annihilator -> of_bool ik annihilator - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - let c_logor = log (||) ~annihilator:true - let c_logand = log (&&) ~annihilator:false - - let log1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_bool i1 with - | Some x -> of_bool ik (f ik x) - | _ -> top_of ik - - let c_lognot = log1 (fun _ik -> not) - - let bit f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - let bitcomp f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{underflow=false; overflow=false})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let logxor = bit (fun _ik -> Ints_t.logxor) - - let logand ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (Ints_t.logand x y) |> fst with Division_by_zero -> top_of ik) - | _, Some y when Ints_t.equal y Ints_t.zero -> of_int ik Ints_t.zero |> fst - | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst - | _ -> top_of ik - - let logor = bit (fun _ik -> Ints_t.logor) - - let bit1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_int i1 with - | Some x -> of_int ik (f ik x) |> fst - | _ -> top_of ik - - let lognot = bit1 (fun _ik -> Ints_t.lognot) - let shift_right = bitcomp (fun _ik x y -> Ints_t.shift_right x (Ints_t.to_int y)) - - let neg ?no_ov ik = function None -> (None,{underflow=false; overflow=false}) | Some x -> norm ik @@ Some (IArith.neg x) - - let binary_op_with_norm ?no_ov op ik x y = match x, y with - | None, None -> (None, {overflow=false; underflow= false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some x, Some y -> norm ik @@ Some (op x y) - - let add ?no_ov = binary_op_with_norm IArith.add - let mul ?no_ov = binary_op_with_norm IArith.mul - let sub ?no_ov = binary_op_with_norm IArith.sub - - let shift_left ik a b = - match is_bot a, is_bot b with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show a) (show b))) - | _ -> - match a, minimal b, maximal b with - | Some a, Some bl, Some bu when (Ints_t.compare bl Ints_t.zero >= 0) -> - (try - let r = IArith.shift_left a (Ints_t.to_int bl, Ints_t.to_int bu) in - norm ik @@ Some r - with Z.Overflow -> (top_of ik,{underflow=false; overflow=true})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let rem ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (xl, xu), Some (yl, yu) -> - if is_top_of ik x && is_top_of ik y then - (* This is needed to preserve soundness also on things bigger than int32 e.g. *) - (* x: 3803957176L -> T in Interval32 *) - (* y: 4209861404L -> T in Interval32 *) - (* x % y: 3803957176L -> T in Interval32 *) - (* T in Interval32 is [-2147483648,2147483647] *) - (* the code below computes [-2147483647,2147483647] for this though which is unsound *) - top_of ik - else - (* If we have definite values, Ints_t.rem will give a definite result. - * Otherwise we meet with a [range] the result can be in. - * This range is [0, min xu b] if x is positive, and [max xl -b, min xu b] if x can be negative. - * The precise bound b is one smaller than the maximum bound. Negative y give the same result as positive. *) - let pos x = if Ints_t.compare x Ints_t.zero < 0 then Ints_t.neg x else x in - let b = Ints_t.sub (Ints_t.max (pos yl) (pos yu)) Ints_t.one in - let range = if Ints_t.compare xl Ints_t.zero>= 0 then Some (Ints_t.zero, Ints_t.min xu b) else Some (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit (fun _ik -> Ints_t.rem) ik x y) range - - let rec div ?no_ov ik x y = - match x, y with - | None, None -> (bot (),{underflow=false; overflow=false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | (Some (x1,x2) as x), (Some (y1,y2) as y) -> - begin - let is_zero v = Ints_t.compare v Ints_t.zero = 0 in - match y1, y2 with - | l, u when is_zero l && is_zero u -> (top_of ik,{underflow=false; overflow=false}) (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> div ik (Some (x1,x2)) (Some (Ints_t.one,y2)) - | _, u when is_zero u -> div ik (Some (x1,x2)) (Some (y1, Ints_t.(neg one))) - | _ when leq (of_int ik (Ints_t.zero) |> fst) (Some (y1,y2)) -> (top_of ik,{underflow=false; overflow=false}) - | _ -> binary_op_with_norm IArith.div ik x y - end - - let ne ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik true - else if Ints_t.compare x2 y1 <= 0 && Ints_t.compare y2 x1 <= 0 then - of_bool ik false - else top_bool - - let eq ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 && Ints_t.compare x2 y1 <= 0 then - of_bool ik true - else if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik false - else top_bool - - let ge ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 then of_bool ik true - else if Ints_t.compare x2 y1 < 0 then of_bool ik false - else top_bool - - let le ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 <= 0 then of_bool ik true - else if Ints_t.compare y2 x1 < 0 then of_bool ik false - else top_bool - - let gt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 then of_bool ik true - else if Ints_t.compare x2 y1 <= 0 then of_bool ik false - else top_bool - - let lt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 < 0 then of_bool ik true - else if Ints_t.compare y2 x1 <= 0 then of_bool ik false - else top_bool - - let invariant_ikind e ik = function - | Some (x1, x2) -> - let (x1', x2') = BatTuple.Tuple2.mapn Ints_t.to_bigint (x1, x2) in - IntInvariant.of_interval e ik (x1', x2') - | None -> Invariant.none - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let shrink = function - | Some (l, u) -> (return None) <+> (GobQCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) - | None -> empty - in - QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) (fun x -> of_interval ik x |> fst ) pair_arb) - - let modulo n k = - let result = Ints_t.rem n k in - if Ints_t.compare result Ints_t.zero >= 0 then result - else Ints_t.add result k - - let refine_with_congruence ik (intv : t) (cong : (int_t * int_t ) option) : t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if Ints_t.equal m Ints_t.zero && (Ints_t.compare c x < 0 || Ints_t.compare c y > 0) then None - else if Ints_t.equal m Ints_t.zero then - Some (c, c) - else - let (min_ik, max_ik) = range ik in - let rcx = - if Ints_t.equal x min_ik then x else - Ints_t.add x (modulo (Ints_t.sub c x) (Ints_t.abs m)) in - let lcy = - if Ints_t.equal y max_ik then y else - Ints_t.sub y (modulo (Ints_t.sub y c) (Ints_t.abs m)) in - if Ints_t.compare rcx lcy > 0 then None - else if Ints_t.equal rcx lcy then norm ik @@ Some (rcx, rcx) |> fst - else norm ik @@ Some (rcx, lcy) |> fst - | _ -> None - - let refine_with_congruence ik x y = - let refn = refine_with_congruence ik x y in - if M.tracing then M.trace "refine" "int_refine_with_congruence %a %a -> %a" pretty x pretty y pretty refn; - refn - - let refine_with_interval ik a b = meet ik a b - - let refine_with_excl_list ik (intv : t) (excl : (int_t list * (int64 * int64)) option) : t = - match intv, excl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls, (rl, rh)) -> - let rec shrink op b = - let new_b = (op b (Ints_t.of_int(Bool.to_int(BatList.mem_cmp Ints_t.compare b ls)))) in - if not (Ints_t.equal b new_b) then shrink op new_b else new_b - in - let (min_ik, max_ik) = range ik in - let l' = if Ints_t.equal l min_ik then l else shrink Ints_t.add l in - let u' = if Ints_t.equal u max_ik then u else shrink Ints_t.sub u in - let intv' = norm ik @@ Some (l', u') |> fst in - let range = norm ~suppress_ovwarn:true ik (Some (Ints_t.of_bigint (Size.min_from_bit_range rl), Ints_t.of_bigint (Size.max_from_bit_range rh))) |> fst in - meet ik intv' range - - let refine_with_incl_list ik (intv: t) (incl : (int_t list) option) : t = - match intv, incl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls) -> - let rec min m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> min (Some x) xs | Some m -> if Ints_t.compare m x < 0 then min (Some m) xs else min (Some x) xs in - let rec max m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> max (Some x) xs | Some m -> if Ints_t.compare m x > 0 then max (Some m) xs else max (Some x) xs in - match min None ls, max None ls with - | Some m1, Some m2 -> refine_with_interval ik (Some(l, u)) (Some (m1, m2)) - | _, _-> intv - - let project ik p t = t -end - -(** IntervalSetFunctor that is not just disjunctive completion of intervals, but attempts to be precise for wraparound arithmetic for unsigned types *) -module IntervalSetFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) list = -struct - - module Interval = IntervalFunctor (Ints_t) - module IArith = IntervalArith (Ints_t) - - - let name () = "interval_sets" - - type int_t = Ints_t.t - - let (>.) a b = Ints_t.compare a b > 0 - let (=.) a b = Ints_t.compare a b = 0 - let (<.) a b = Ints_t.compare a b < 0 - let (>=.) a b = Ints_t.compare a b >= 0 - let (<=.) a b = Ints_t.compare a b <= 0 - let (+.) a b = Ints_t.add a b - let (-.) a b = Ints_t.sub a b - - (* - Each domain's element is guaranteed to be in canonical form. That is, each interval contained - inside the set does not overlap with each other and they are not adjacent. - *) - type t = (Ints_t.t * Ints_t.t) list [@@deriving eq, hash, ord] - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - - let top_of ik = [range ik] - - let bot () = [] - - let bot_of ik = bot () - - let show (x: t) = - let show_interval i = Printf.sprintf "[%s, %s]" (Ints_t.to_string (fst i)) (Ints_t.to_string (snd i)) in - List.fold_left (fun acc i -> (show_interval i) :: acc) [] x |> List.rev |> String.concat ", " |> Printf.sprintf "[%s]" - - (* New type definition for the sweeping line algorithm used for implementing join/meet functions. *) - type event = Enter of Ints_t.t | Exit of Ints_t.t - - let unbox_event = function Enter x -> x | Exit x -> x - - let cmp_events x y = - (* Deliberately comparing ints first => Cannot be derived *) - let res = Ints_t.compare (unbox_event x) (unbox_event y) in - if res <> 0 then res - else - begin - match (x, y) with - | (Enter _, Exit _) -> -1 - | (Exit _, Enter _) -> 1 - | (_, _) -> 0 - end - - let interval_set_to_events (xs: t) = - List.concat_map (fun (a, b) -> [Enter a; Exit b]) xs - - let two_interval_sets_to_events (xs: t) (ys: t) = - let xs = interval_set_to_events xs in - let ys = interval_set_to_events ys in - List.merge cmp_events xs ys - - (* Using the sweeping line algorithm, combined_event_list returns a new event list representing the intervals in which at least n intervals in xs overlap - This function is used for both join and meet operations with different parameter n: 1 for join, 2 for meet *) - let combined_event_list lattice_op (xs:event list) = - let l = match lattice_op with `Join -> 1 | `Meet -> 2 in - let aux (interval_count, acc) = function - | Enter x -> (interval_count + 1, if (interval_count + 1) >= l && interval_count < l then (Enter x)::acc else acc) - | Exit x -> (interval_count - 1, if interval_count >= l && (interval_count - 1) < l then (Exit x)::acc else acc) - in - List.fold_left aux (0, []) xs |> snd |> List.rev - - let rec events_to_intervals = function - | [] -> [] - | (Enter x)::(Exit y)::xs -> (x, y)::(events_to_intervals xs) - | _ -> failwith "Invalid events list" - - let remove_empty_gaps (xs: t) = - let aux acc (l, r) = match acc with - | ((a, b)::acc') when (b +. Ints_t.one) >=. l -> (a, r)::acc' - | _ -> (l, r)::acc - in - List.fold_left aux [] xs |> List.rev - - let canonize (xs: t) = - interval_set_to_events xs |> - List.sort cmp_events |> - combined_event_list `Join |> - events_to_intervals |> - remove_empty_gaps - - let unop (x: t) op = match x with - | [] -> [] - | _ -> canonize @@ List.concat_map op x - - let binop (x: t) (y: t) op : t = match x, y with - | [], _ -> [] - | _, [] -> [] - | _, _ -> canonize @@ List.concat_map op (BatList.cartesian_product x y) - - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let minimal = function - | [] -> None - | (x, _)::_ -> Some x - - let maximal = function - | [] -> None - | xs -> Some (BatList.last xs |> snd) - - let equal_to_interval i (a, b) = - if a =. b && b =. i then - `Eq - else if a <=. i && i <=. b then - `Top - else - `Neq - - let equal_to i xs = match List.map (equal_to_interval i) xs with - | [] -> failwith "unsupported: equal_to with bottom" - | [`Eq] -> `Eq - | ys when List.for_all ((=) `Neq) ys -> `Neq - | _ -> `Top - - let norm_interval ?(suppress_ovwarn=false) ?(cast=false) ik (x,y) : t*overflow_info = - if x >. y then - ([],{underflow=false; overflow=false}) - else - let (min_ik, max_ik) = range ik in - let underflow = min_ik >. x in - let overflow = max_ik <. y in - let v = if underflow || overflow then - begin - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (max_ik -. min_ik) in - let resdiff = Ints_t.abs (y -. x) in - if resdiff >. diff then - [range ik] - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if l <=. u then - [(l, u)] - else - (* Interval that wraps around (begins to the right of its end). We CAN represent such intervals *) - [(min_ik, u); (l, max_ik)] - else if not cast && should_ignore_overflow ik then - [Ints_t.max min_ik x, Ints_t.min max_ik y] - else - [range ik] - end - else - [(x,y)] - in - if suppress_ovwarn then (v, {underflow=false; overflow=false}) else (v, {underflow; overflow}) - - let norm_intvs ?(suppress_ovwarn=false) ?(cast=false) (ik:ikind) (xs: t) : t*overflow_info = - let res = List.map (norm_interval ~suppress_ovwarn ~cast ik) xs in - let intvs = List.concat_map fst res in - let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in - let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in - (canonize intvs,{underflow; overflow}) - - let binary_op_with_norm op (ik:ikind) (x: t) (y: t) : t*overflow_info = match x, y with - | [], _ -> ([],{overflow=false; underflow=false}) - | _, [] -> ([],{overflow=false; underflow=false}) - | _, _ -> norm_intvs ik @@ List.concat_map (fun (x,y) -> [op x y]) (BatList.cartesian_product x y) - - let binary_op_with_ovc (x: t) (y: t) op : t*overflow_info = match x, y with - | [], _ -> ([],{overflow=false; underflow=false}) - | _, [] -> ([],{overflow=false; underflow=false}) - | _, _ -> - let res = List.map op (BatList.cartesian_product x y) in - let intvs = List.concat_map fst res in - let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in - let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in - (canonize intvs,{underflow; overflow}) - - let unary_op_with_norm op (ik:ikind) (x: t) = match x with - | [] -> ([],{overflow=false; underflow=false}) - | _ -> norm_intvs ik @@ List.concat_map (fun x -> [op x]) x - - let rec leq (xs: t) (ys: t) = - let leq_interval (al, au) (bl, bu) = al >=. bl && au <=. bu in - match xs, ys with - | [], _ -> true - | _, [] -> false - | (xl,xr)::xs', (yl,yr)::ys' -> - if leq_interval (xl,xr) (yl,yr) then - leq xs' ys - else if xr <. yl then - false - else - leq xs ys' - - let join ik (x: t) (y: t): t = - two_interval_sets_to_events x y |> - combined_event_list `Join |> - events_to_intervals |> - remove_empty_gaps - - let meet ik (x: t) (y: t): t = - two_interval_sets_to_events x y |> - combined_event_list `Meet |> - events_to_intervals - - let to_int = function - | [x] -> IArith.to_int x - | _ -> None - - let zero = [IArith.zero] - let one = [IArith.one] - let top_bool = [IArith.top_bool] - - let not_bool (x:t) = - let is_false x = equal x zero in - let is_true x = equal x one in - if is_true x then zero else if is_false x then one else top_bool - - let to_bool = function - | [(l,u)] when l =. Ints_t.zero && u =. Ints_t.zero -> Some false - | x -> if leq zero x then None else Some true - - let of_bool _ = function true -> one | false -> zero - - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm_interval ~suppress_ovwarn ~cast:false ik (x,y) - - let of_int ik (x: int_t) = of_interval ik (x, x) - - let lt ik x y = - match x, y with - | [], [] -> bot_of ik - | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> - let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in - let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in - if max_x <. min_y then - of_bool ik true - else if min_x >=. max_y then - of_bool ik false - else - top_bool - - let le ik x y = - match x, y with - | [], [] -> bot_of ik - | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> - let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in - let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in - if max_x <=. min_y then - of_bool ik true - else if min_x >. max_y then - of_bool ik false - else - top_bool - - let gt ik x y = not_bool @@ le ik x y - - let ge ik x y = not_bool @@ lt ik x y - - let eq ik x y = match x, y with - | (a, b)::[], (c, d)::[] when a =. b && c =. d && a =. c -> - one - | _ -> - if is_bot (meet ik x y) then - zero - else - top_bool - - let ne ik x y = not_bool @@ eq ik x y - let interval_to_int i = Interval.to_int (Some i) - let interval_to_bool i = Interval.to_bool (Some i) - - let log f ik (i1, i2) = - match (interval_to_bool i1, interval_to_bool i2) with - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - - let bit f ik (i1, i2) = - match (interval_to_int i1), (interval_to_int i2) with - | Some x, Some y -> (try of_int ik (f x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - - let bitcomp f ik (i1, i2) = - match (interval_to_int i1, interval_to_int i2) with - | Some x, Some y -> (try of_int ik (f x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{overflow=false; underflow=false})) - | _, _ -> (top_of ik,{overflow=false; underflow=false}) - - let logand ik x y = - let interval_logand = bit Ints_t.logand ik in - binop x y interval_logand - - let logor ik x y = - let interval_logor = bit Ints_t.logor ik in - binop x y interval_logor - - let logxor ik x y = - let interval_logxor = bit Ints_t.logxor ik in - binop x y interval_logxor - - let lognot ik x = - let interval_lognot i = - match interval_to_int i with - | Some x -> of_int ik (Ints_t.lognot x) |> fst - | _ -> top_of ik - in - unop x interval_lognot - - let shift_left ik x y = - let interval_shiftleft = bitcomp (fun x y -> Ints_t.shift_left x (Ints_t.to_int y)) ik in - binary_op_with_ovc x y interval_shiftleft - - let shift_right ik x y = - let interval_shiftright = bitcomp (fun x y -> Ints_t.shift_right x (Ints_t.to_int y)) ik in - binary_op_with_ovc x y interval_shiftright - - let c_lognot ik x = - let log1 f ik i1 = - match interval_to_bool i1 with - | Some x -> of_bool ik (f x) - | _ -> top_of ik - in - let interval_lognot = log1 not ik in - unop x interval_lognot - - let c_logand ik x y = - let interval_logand = log (&&) ik in - binop x y interval_logand - - let c_logor ik x y = - let interval_logor = log (||) ik in - binop x y interval_logor - - let add ?no_ov = binary_op_with_norm IArith.add - let sub ?no_ov = binary_op_with_norm IArith.sub - let mul ?no_ov = binary_op_with_norm IArith.mul - let neg ?no_ov = unary_op_with_norm IArith.neg - - let div ?no_ov ik x y = - let rec interval_div x (y1, y2) = begin - let top_of ik = top_of ik |> List.hd in - let is_zero v = v =. Ints_t.zero in - match y1, y2 with - | l, u when is_zero l && is_zero u -> top_of ik (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> interval_div x (Ints_t.one,y2) - | _, u when is_zero u -> interval_div x (y1, Ints_t.(neg one)) - | _ when leq (of_int ik (Ints_t.zero) |> fst) ([(y1,y2)]) -> top_of ik - | _ -> IArith.div x (y1, y2) - end - in binary_op_with_norm interval_div ik x y - - let rem ik x y = - let interval_rem (x, y) = - if Interval.is_top_of ik (Some x) && Interval.is_top_of ik (Some y) then - top_of ik - else - let (xl, xu) = x in let (yl, yu) = y in - let pos x = if x <. Ints_t.zero then Ints_t.neg x else x in - let b = (Ints_t.max (pos yl) (pos yu)) -. Ints_t.one in - let range = if xl >=. Ints_t.zero then (Ints_t.zero, Ints_t.min xu b) else (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit Ints_t.rem ik (x, y)) [range] - in - binop x y interval_rem - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik x = norm_intvs ~cast:true ik x - - (* - narrows down the extremeties of xs if they are equal to boundary values of the ikind with (possibly) narrower values from ys - *) - let narrow ik xs ys = match xs ,ys with - | [], _ -> [] | _ ,[] -> xs - | _, _ -> - let min_xs = minimal xs |> Option.get in - let max_xs = maximal xs |> Option.get in - let min_ys = minimal ys |> Option.get in - let max_ys = maximal ys |> Option.get in - let min_range,max_range = range ik in - let threshold = get_interval_threshold_widening () in - let min = if min_xs =. min_range || threshold && min_ys >. min_xs && IArith.is_lower_threshold min_xs then min_ys else min_xs in - let max = if max_xs =. max_range || threshold && max_ys <. max_xs && IArith.is_upper_threshold max_xs then max_ys else max_xs in - xs - |> (function (_, y)::z -> (min, y)::z | _ -> []) - |> List.rev - |> (function (x, _)::z -> (x, max)::z | _ -> []) - |> List.rev - - (* - 1. partitions the intervals of xs by assigning each of them to the an interval in ys that includes it. - and joins all intervals in xs assigned to the same interval in ys as one interval. - 2. checks for every pair of adjacent pairs whether the pairs did approach (if you compare the intervals from xs and ys) and merges them if it is the case. - 3. checks whether partitions at the extremeties are approaching infinity (and expands them to infinity. in that case) - - The expansion (between a pair of adjacent partitions or at extremeties ) stops at a threshold. - *) - let widen ik xs ys = - let (min_ik,max_ik) = range ik in - let threshold = get_bool "ana.int.interval_threshold_widening" in - let upper_threshold (_,u) = IArith.upper_threshold u max_ik in - let lower_threshold (l,_) = IArith.lower_threshold l min_ik in - (*obtain partitioning of xs intervals according to the ys interval that includes them*) - let rec interval_sets_to_partitions (ik: ikind) (acc : (int_t * int_t) option) (xs: t) (ys: t)= - match xs,ys with - | _, [] -> [] - | [], (y::ys) -> (acc,y):: interval_sets_to_partitions ik None [] ys - | (x::xs), (y::ys) when Interval.leq (Some x) (Some y) -> interval_sets_to_partitions ik (Interval.join ik acc (Some x)) xs (y::ys) - | (x::xs), (y::ys) -> (acc,y) :: interval_sets_to_partitions ik None (x::xs) ys - in - let interval_sets_to_partitions ik xs ys = interval_sets_to_partitions ik None xs ys in - (*merge a pair of adjacent partitions*) - let merge_pair ik (a,b) (c,d) = - let new_a = function - | None -> Some (upper_threshold b, upper_threshold b) - | Some (ax,ay) -> Some (ax, upper_threshold b) - in - let new_c = function - | None -> Some (lower_threshold d, lower_threshold d) - | Some (cx,cy) -> Some (lower_threshold d, cy) - in - if threshold && (lower_threshold d +. Ints_t.one) >. (upper_threshold b) then - [(new_a a,(fst b, upper_threshold b)); (new_c c, (lower_threshold d, snd d))] - else - [(Interval.join ik a c, (Interval.join ik (Some b) (Some d) |> Option.get))] - in - let partitions_are_approaching part_left part_right = match part_left, part_right with - | (Some (_, left_x), (_, left_y)), (Some (right_x, _), (right_y, _)) -> (right_x -. left_x) >. (right_y -. left_y) - | _,_ -> false - in - (*merge all approaching pairs of adjacent partitions*) - let rec merge_list ik = function - | [] -> [] - | x::y::xs when partitions_are_approaching x y -> merge_list ik ((merge_pair ik x y) @ xs) - | x::xs -> x :: merge_list ik xs - in - (*expands left extremity*) - let widen_left = function - | [] -> [] - | (None,(lb,rb))::ts -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (None, (lt,rb))::ts - | (Some (la,ra), (lb,rb))::ts when lb <. la -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (Some (la,ra),(lt,rb))::ts - | x -> x - in - (*expands right extremity*) - let widen_right x = - let map_rightmost = function - | [] -> [] - | (None,(lb,rb))::ts -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (None, (lb,ut))::ts - | (Some (la,ra), (lb,rb))::ts when ra <. rb -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (Some (la,ra),(lb,ut))::ts - | x -> x - in - List.rev x |> map_rightmost |> List.rev - in - interval_sets_to_partitions ik xs ys |> merge_list ik |> widen_left |> widen_right |> List.map snd - - let starting ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (fst (range ik), n) - - let invariant_ikind e ik xs = - List.map (fun x -> Interval.invariant_ikind e ik (Some x)) xs |> - let open Invariant in List.fold_left (||) (bot ()) - - let modulo n k = - let result = Ints_t.rem n k in - if result >=. Ints_t.zero then result - else result +. k - - let refine_with_congruence ik (intvs: t) (cong: (int_t * int_t ) option): t = - let refine_with_congruence_interval ik (cong : (int_t * int_t ) option) (intv : (int_t * int_t ) option): t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if m =. Ints_t.zero && (c <. x || c >. y) then [] - else if m =. Ints_t.zero then - [(c, c)] - else - let (min_ik, max_ik) = range ik in - let rcx = - if x =. min_ik then x else - x +. (modulo (c -. x) (Ints_t.abs m)) in - let lcy = - if y =. max_ik then y else - y -. (modulo (y -. c) (Ints_t.abs m)) in - if rcx >. lcy then [] - else if rcx =. lcy then norm_interval ik (rcx, rcx) |> fst - else norm_interval ik (rcx, lcy) |> fst - | _ -> [] - in - List.concat_map (fun x -> refine_with_congruence_interval ik cong (Some x)) intvs - - let refine_with_interval ik xs = function None -> [] | Some (a,b) -> meet ik xs [(a,b)] - - let refine_with_incl_list ik intvs = function - | None -> intvs - | Some xs -> meet ik intvs (List.map (fun x -> (x,x)) xs) - - let excl_range_to_intervalset (ik: ikind) ((min, max): int_t * int_t) (excl: int_t): t = - let intv1 = (min, excl -. Ints_t.one) in - let intv2 = (excl +. Ints_t.one, max) in - norm_intvs ik ~suppress_ovwarn:true [intv1 ; intv2] |> fst - - let of_excl_list ik (excls: int_t list) = - let excl_list = List.map (excl_range_to_intervalset ik (range ik)) excls in - let res = List.fold_left (meet ik) (top_of ik) excl_list in - res - - let refine_with_excl_list ik (intv : t) = function - | None -> intv - | Some (xs, range) -> - let excl_to_intervalset (ik: ikind) ((rl, rh): (int64 * int64)) (excl: int_t): t = - excl_range_to_intervalset ik (Ints_t.of_bigint (Size.min_from_bit_range rl),Ints_t.of_bigint (Size.max_from_bit_range rh)) excl - in - let excl_list = List.map (excl_to_intervalset ik range) xs in - List.fold_left (meet ik) intv excl_list - - let project ik p t = t - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let list_pair_arb = QCheck.small_list pair_arb in - let canonize_randomly_generated_list = (fun x -> norm_intvs ik x |> fst) in - let shrink xs = GobQCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list - in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) canonize_randomly_generated_list list_pair_arb) -end - -module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t = struct - include D - - let add ?no_ov ik x y = fst @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = fst @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = fst @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = fst @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = fst @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = fst @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = fst @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = fst @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = fst @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = fst @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = fst @@ D.shift_left ik x y - - let shift_right ik x y = fst @@ D.shift_right ik x y -end - -module IntIkind = struct let ikind () = Cil.IInt end -module Interval = IntervalFunctor (IntOps.BigIntOps) -module Interval32 = IntDomWithDefaultIkind (IntDomLifter (SOverflowUnlifter (IntervalFunctor (IntOps.Int64Ops)))) (IntIkind) -module IntervalSet = IntervalSetFunctor (IntOps.BigIntOps) -module Integers (Ints_t : IntOps.IntOps): IkindUnawareS with type t = Ints_t.t and type int_t = Ints_t.t = (* no top/bot, order is <= *) -struct - include Printable.Std - let name () = "integers" - type t = Ints_t.t [@@deriving eq, ord, hash] - type int_t = Ints_t.t - let top () = raise Unknown - let bot () = raise Error - let top_of ik = top () - let bot_of ik = bot () - let show (x: Ints_t.t) = Ints_t.to_string x - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - (* is_top and is_bot are never called, but if they were, the Std impl would raise their exception, so we overwrite them: *) - let is_top _ = false - let is_bot _ = false - - let equal_to i x = if i > x then `Neq else `Top - let leq x y = x <= y - let join x y = if Ints_t.compare x y > 0 then x else y - let widen = join - let meet x y = if Ints_t.compare x y > 0 then y else x - let narrow = meet - - let of_bool x = if x then Ints_t.one else Ints_t.zero - let to_bool' x = x <> Ints_t.zero - let to_bool x = Some (to_bool' x) - let of_int x = x - let to_int x = Some x - - let neg = Ints_t.neg - let add = Ints_t.add (* TODO: signed overflow is undefined behavior! *) - let sub = Ints_t.sub - let mul = Ints_t.mul - let div = Ints_t.div - let rem = Ints_t.rem - let lt n1 n2 = of_bool (n1 < n2) - let gt n1 n2 = of_bool (n1 > n2) - let le n1 n2 = of_bool (n1 <= n2) - let ge n1 n2 = of_bool (n1 >= n2) - let eq n1 n2 = of_bool (n1 = n2) - let ne n1 n2 = of_bool (n1 <> n2) - let lognot = Ints_t.lognot - let logand = Ints_t.logand - let logor = Ints_t.logor - let logxor = Ints_t.logxor - let shift_left n1 n2 = Ints_t.shift_left n1 (Ints_t.to_int n2) - let shift_right n1 n2 = Ints_t.shift_right n1 (Ints_t.to_int n2) - let c_lognot n1 = of_bool (not (to_bool' n1)) - let c_logand n1 n2 = of_bool ((to_bool' n1) && (to_bool' n2)) - let c_logor n1 n2 = of_bool ((to_bool' n1) || (to_bool' n2)) - let cast_to ?(suppress_ovwarn=false) ?torg t x = failwith @@ "Cast_to not implemented for " ^ (name ()) ^ "." - let arbitrary ik = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 (* TODO: use ikind *) - let invariant _ _ = Invariant.none (* TODO *) -end - -module FlatPureIntegers: IkindUnawareS with type t = int64 and type int_t = int64 = (* Integers, but raises Unknown/Error on join/meet *) -struct - include Integers(IntOps.Int64Ops) - let top () = raise Unknown - let bot () = raise Error - let leq = equal - let pretty_diff () (x,y) = Pretty.dprintf "Integer %a instead of %a" pretty x pretty y - let join x y = if equal x y then x else top () - let meet x y = if equal x y then x else bot () -end - -module Flat (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Lift, but goes to `Top/`Bot if Base raises Unknown/Error *) -struct - type int_t = Base.int_t - include Lattice.FlatConf (struct - include Printable.DefaultConf - let top_name = "Unknown int" - let bot_name = "Error int" - end) (Base) - - let top_of ik = top () - let bot_of ik = bot () - - - let name () = "flat integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ikind x = top_of ikind - let ending ?(suppress_ovwarn=false) ikind x = top_of ikind - let maximal x = None - let minimal x = None - - let lift1 f x = match x with - | `Lifted x -> - (try `Lifted (f x) with Unknown -> `Top | Error -> `Bot) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> - (try `Lifted (f x y) with Unknown -> `Top | Error -> `Bot) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Lift (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Flat, but does not go to `Top/Bot` if Base raises Unknown/Error *) -struct - include Lattice.LiftPO (struct - include Printable.DefaultConf - let top_name = "MaxInt" - let bot_name = "MinInt" - end) (Base) - type int_t = Base.int_t - let top_of ik = top () - let bot_of ik = bot () - include StdTop (struct type nonrec t = t let top_of = top_of end) - - let name () = "lifted integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let lift1 f x = match x with - | `Lifted x -> `Lifted (f x) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> `Lifted (f x y) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Flattened = Flat (Integers (IntOps.Int64Ops)) -module Lifted = Lift (Integers (IntOps.Int64Ops)) - -module Reverse (Base: IkindUnawareS) = -struct - include Base - include (Lattice.Reverse (Base) : Lattice.S with type t := Base.t) -end - -module BISet = struct - include SetDomain.Make (IntOps.BigIntOps) - let is_singleton s = cardinal s = 1 -end - -(* The module [Exclusion] constains common functionality about handling of exclusion sets between [DefExc] and [Enums] *) -module Exclusion = -struct - module R = Interval32 - (* We use these types for the functions in this module to make the intended meaning more explicit *) - type t = Exc of BISet.t * Interval32.t - type inc = Inc of BISet.t [@@unboxed] - let max_of_range r = Size.max_from_bit_range (Option.get (R.maximal r)) - let min_of_range r = Size.min_from_bit_range (Option.get (R.minimal r)) - let cardinality_of_range r = Z.succ (Z.add (Z.neg (min_of_range r)) (max_of_range r)) - - let cardinality_BISet s = - Z.of_int (BISet.cardinal s) - - let leq_excl_incl (Exc (xs, r)) (Inc ys) = - (* For a <= b to hold, the cardinalities must fit, i.e. |a| <= |b|, which implies |min_r, max_r| - |xs| <= |ys|. We check this first. *) - let lower_bound_cardinality_a = Z.sub (cardinality_of_range r) (cardinality_BISet xs) in - let card_b = cardinality_BISet ys in - if Z.compare lower_bound_cardinality_a card_b > 0 then - false - else (* The cardinality did fit, so we check for all elements that are represented by range r, whether they are in (xs union ys) *) - let min_a = min_of_range r in - let max_a = max_of_range r in - GobZ.for_all_range (fun el -> BISet.mem el xs || BISet.mem el ys) (min_a, max_a) - - let leq (Exc (xs, r)) (Exc (ys, s)) = - let min_a, max_a = min_of_range r, max_of_range r in - let excluded_check = BISet.for_all (fun y -> BISet.mem y xs || Z.compare y min_a < 0 || Z.compare y max_a > 0) ys in (* if true, then the values ys, that are not in b, also do not occur in a *) - if not excluded_check - then false - else begin (* Check whether all elements that are in the range r, but not in s, are in xs, i.e. excluded. *) - if R.leq r s then true - else begin if Z.compare (cardinality_BISet xs) (Z.sub (cardinality_of_range r) (cardinality_of_range s)) >= 0 (* Check whether the number of excluded elements in a is as least as big as |min_r, max_r| - |min_s, max_s| *) - then - let min_b, max_b = min_of_range s, max_of_range s in - let leq1 = (* check whether the elements in [r_l; s_l-1] are all in xs, i.e. excluded *) - if Z.compare min_a min_b < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (min_a, Z.pred min_b) - else - true - in - let leq2 () = (* check whether the elements in [s_u+1; r_u] are all in xs, i.e. excluded *) - if Z.compare max_b max_a < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (Z.succ max_b, max_a) - else - true - in - leq1 && (leq2 ()) - else - false - end - end -end - -module DefExc : S with type int_t = Z.t = (* definite or set of excluded values *) -struct - module S = BISet - module R = Interval32 (* range for exclusion *) - - (* Ikind used for intervals representing the domain *) - let range_ikind = Cil.IInt - let size t = R.of_interval range_ikind (let a,b = Size.bits_i64 t in Int64.neg a,b) - - - type t = [ - | `Excluded of S.t * R.t - | `Definite of Z.t - | `Bot - ] [@@deriving eq, ord, hash] - type int_t = Z.t - let name () = "def_exc" - - - let top_range = R.of_interval range_ikind (-99L, 99L) (* Since there is no top ikind we use a range that includes both ILongLong [-63,63] and IULongLong [0,64]. Only needed for intermediate range computation on longs. Correct range is set by cast. *) - let top () = `Excluded (S.empty (), top_range) - let bot () = `Bot - let top_of ik = `Excluded (S.empty (), size ik) - let bot_of ik = bot () - - let show x = - let short_size x = "("^R.show x^")" in - match x with - | `Bot -> "Error int" - | `Definite x -> Z.to_string x - (* Print the empty exclusion as if it was a distinct top element: *) - | `Excluded (s,l) when S.is_empty s -> "Unknown int" ^ short_size l - (* Prepend the exclusion sets with something: *) - | `Excluded (s,l) -> "Not " ^ S.show s ^ short_size l - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let maximal = function - | `Definite x -> Some x - | `Excluded (s,r) -> Some (Exclusion.max_of_range r) - | `Bot -> None - - let minimal = function - | `Definite x -> Some x - | `Excluded (s,r) -> Some (Exclusion.min_of_range r) - | `Bot -> None - - let in_range r i = - if Z.compare i Z.zero < 0 then - let lowerb = Exclusion.min_of_range r in - Z.compare lowerb i <= 0 - else - let upperb = Exclusion.max_of_range r in - Z.compare i upperb <= 0 - - let is_top x = x = top () - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Definite x -> if i = x then `Eq else `Neq - | `Excluded (s,r) -> if S.mem i s then `Neq else `Top - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik = function - | `Excluded (s,r) -> - let r' = size ik in - if R.leq r r' then (* upcast -> no change *) - `Excluded (s, r) - else if ik = IBool then (* downcast to bool *) - if S.mem Z.zero s then - `Definite Z.one - else - `Excluded (S.empty(), r') - else - (* downcast: may overflow *) - (* let s' = S.map (Size.cast ik) s in *) - (* We want to filter out all i in s' where (t)x with x in r could be i. *) - (* Since this is hard to compute, we just keep all i in s' which overflowed, since those are safe - all i which did not overflow may now be possible due to overflow of r. *) - (* S.diff s' s, r' *) - (* The above is needed for test 21/03, but not sound! See example https://github.com/goblint/analyzer/pull/95#discussion_r483023140 *) - `Excluded (S.empty (), r') - | `Definite x -> `Definite (Size.cast ik x) - | `Bot -> `Bot - - (* Wraps definite values and excluded values according to the ikind. - * For an `Excluded s,r , assumes that r is already an overapproximation of the range of possible values. - * r might be larger than the possible range of this type; the range of the returned `Excluded set will be within the bounds of the ikind. - *) - let norm ik v = - match v with - | `Excluded (s, r) -> - let possibly_overflowed = not (R.leq r (size ik)) || not (S.for_all (in_range (size ik)) s) in - (* If no overflow occurred, just return x *) - if not possibly_overflowed then ( - v - ) - (* Else, if an overflow might have occurred but we should just ignore it *) - else if should_ignore_overflow ik then ( - let r = size ik in - (* filter out excluded elements that are not in the range *) - let mapped_excl = S.filter (in_range r) s in - `Excluded (mapped_excl, r) - ) - (* Else, if an overflow occurred that we should not treat with wrap-around, go to top *) - else if not (should_wrap ik) then ( - top_of ik - ) else ( - (* Else an overflow occurred that we should treat with wrap-around *) - let r = size ik in - (* Perform a wrap-around for unsigned values and for signed values (if configured). *) - let mapped_excl = S.map (fun excl -> Size.cast ik excl) s in - match ik with - | IBool -> - begin match S.mem Z.zero mapped_excl, S.mem Z.one mapped_excl with - | false, false -> `Excluded (mapped_excl, r) (* Not {} -> Not {} *) - | true, false -> `Definite Z.one (* Not {0} -> 1 *) - | false, true -> `Definite Z.zero (* Not {1} -> 0 *) - | true, true -> `Bot (* Not {0, 1} -> bot *) - end - | ik -> - `Excluded (mapped_excl, r) - ) - | `Definite x -> - let min, max = Size.range ik in - (* Perform a wrap-around for unsigned values and for signed values (if configured). *) - if should_wrap ik then ( - cast_to ik v - ) - else if Z.compare min x <= 0 && Z.compare x max <= 0 then ( - v - ) - else if should_ignore_overflow ik then ( - M.warn ~category:M.Category.Integer.overflow "DefExc: Value was outside of range, indicating overflow, but 'sem.int.signed_overflow' is 'assume_none' -> Returned Bot"; - `Bot - ) - else ( - top_of ik - ) - | `Bot -> `Bot - - let leq x y = match (x,y) with - (* `Bot <= x is always true *) - | `Bot, _ -> true - (* Anything except bot <= bot is always false *) - | _, `Bot -> false - (* Two known values are leq whenever equal *) - | `Definite (x: int_t), `Definite y -> x = y - (* A definite value is leq all exclusion sets that don't contain it *) - | `Definite x, `Excluded (s,r) -> in_range r x && not (S.mem x s) - (* No finite exclusion set can be leq than a definite value *) - | `Excluded (xs, xr), `Definite d -> - Exclusion.(leq_excl_incl (Exc (xs, xr)) (Inc (S.singleton d))) - | `Excluded (xs,xr), `Excluded (ys,yr) -> - Exclusion.(leq (Exc (xs,xr)) (Exc (ys, yr))) - - let join' ?range ik x y = - match (x,y) with - (* The least upper bound with the bottom element: *) - | `Bot, x -> x - | x, `Bot -> x - (* The case for two known values: *) - | `Definite (x: int_t), `Definite y -> - (* If they're equal, it's just THAT value *) - if x = y then `Definite x - (* Unless one of them is zero, we can exclude it: *) - else - let a,b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval range_ikind a) (R.of_interval range_ikind b) in - `Excluded ((if Z.equal x Z.zero || Z.equal y Z.zero then S.empty () else S.singleton Z.zero), r) - (* A known value and an exclusion set... the definite value should no - * longer be excluded: *) - | `Excluded (s,r), `Definite x - | `Definite x, `Excluded (s,r) -> - if not (in_range r x) then - let a = R.of_interval range_ikind (Size.min_range_sign_agnostic x) in - `Excluded (S.remove x s, R.join a r) - else - `Excluded (S.remove x s, r) - (* For two exclusion sets, only their intersection can be excluded: *) - | `Excluded (x,wx), `Excluded (y,wy) -> `Excluded (S.inter x y, range |? R.join wx wy) - - let join ik = join' ik - - - let widen ik x y = - if get_def_exc_widen_by_join () then - join' ik x y - else if equal x y then - x - else - join' ~range:(size ik) ik x y - - - let meet ik x y = - match (x,y) with - (* Greatest LOWER bound with the least element is trivial: *) - | `Bot, _ -> `Bot - | _, `Bot -> `Bot - (* Definite elements are either equal or the glb is bottom *) - | `Definite x, `Definite y -> if x = y then `Definite x else `Bot - (* The glb of a definite element and an exclusion set is either bottom or - * just the element itself, if it isn't in the exclusion set *) - | `Excluded (s,r), `Definite x - | `Definite x, `Excluded (s,r) -> if S.mem x s || not (in_range r x) then `Bot else `Definite x - (* The greatest lower bound of two exclusion sets is their union, this is - * just DeMorgans Law *) - | `Excluded (x,r1), `Excluded (y,r2) -> - let r' = R.meet r1 r2 in - let s' = S.union x y |> S.filter (in_range r') in - `Excluded (s', r') - - let narrow ik x y = x - - let of_int ik x = norm ik @@ `Definite x - let to_int x = match x with - | `Definite x -> Some x - | _ -> None - - let from_excl ikind (s: S.t) = norm ikind @@ `Excluded (s, size ikind) - - let of_bool_cmp ik x = of_int ik (if x then Z.one else Z.zero) - let of_bool = of_bool_cmp - let to_bool x = - match x with - | `Definite x -> Some (IntOps.BigIntOps.to_bool x) - | `Excluded (s,r) when S.mem Z.zero s -> Some true - | _ -> None - let top_bool = `Excluded (S.empty (), R.of_interval range_ikind (0L, 1L)) - - let of_interval ?(suppress_ovwarn=false) ik (x,y) = - if Z.compare x y = 0 then - of_int ik x - else - let a, b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval ~suppress_ovwarn range_ikind a) (R.of_interval ~suppress_ovwarn range_ikind b) in - let ex = if Z.gt x Z.zero || Z.lt y Z.zero then S.singleton Z.zero else S.empty () in - norm ik @@ (`Excluded (ex, r)) - - let starting ?(suppress_ovwarn=false) ikind x = - let _,u_ik = Size.range ikind in - of_interval ~suppress_ovwarn ikind (x, u_ik) - - let ending ?(suppress_ovwarn=false) ikind x = - let l_ik,_ = Size.range ikind in - of_interval ~suppress_ovwarn ikind (l_ik, x) - - let of_excl_list t l = - let r = size t in (* elements in l are excluded from the full range of t! *) - `Excluded (List.fold_right S.add l (S.empty ()), r) - let is_excl_list l = match l with `Excluded _ -> true | _ -> false - let to_excl_list (x:t) = match x with - | `Definite _ -> None - | `Excluded (s,r) -> Some (S.elements s, (Option.get (R.minimal r), Option.get (R.maximal r))) - | `Bot -> None - - let to_incl_list x = match x with - | `Definite x -> Some [x] - | `Excluded _ -> None - | `Bot -> None - - let apply_range f r = (* apply f to the min/max of the old range r to get a new range *) - (* If the Int64 might overflow on us during computation, we instead go to top_range *) - match R.minimal r, R.maximal r with - | _ -> - let rf m = (size % Size.min_for % f) (m r) in - let r1, r2 = rf Exclusion.min_of_range, rf Exclusion.max_of_range in - R.join r1 r2 - - (* Default behaviour for unary operators, simply maps the function to the - * DefExc data structure. *) - let lift1 f ik x = norm ik @@ match x with - | `Excluded (s,r) -> - let s' = S.map f s in - `Excluded (s', apply_range f r) - | `Definite x -> `Definite (f x) - | `Bot -> `Bot - - let lift2 f ik x y = norm ik (match x,y with - (* We don't bother with exclusion sets: *) - | `Excluded _, `Definite _ - | `Definite _, `Excluded _ - | `Excluded _, `Excluded _ -> top () - (* The good case: *) - | `Definite x, `Definite y -> - (try `Definite (f x y) with | Division_by_zero -> top ()) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) - - (* Default behaviour for binary operators that are injective in either - * argument, so that Exclusion Sets can be used: *) - let lift2_inj f ik x y = - let def_exc f x s r = `Excluded (S.map (f x) s, apply_range (f x) r) in - norm ik @@ - match x,y with - (* If both are exclusion sets, there isn't anything we can do: *) - | `Excluded _, `Excluded _ -> top () - (* A definite value should be applied to all members of the exclusion set *) - | `Definite x, `Excluded (s,r) -> def_exc f x s r - (* Same thing here, but we should flip the operator to map it properly *) - | `Excluded (s,r), `Definite x -> def_exc (Batteries.flip f) x s r - (* The good case: *) - | `Definite x, `Definite y -> `Definite (f x y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - (* The equality check: *) - let eq ik x y = match x,y with - (* Not much to do with two exclusion sets: *) - | `Excluded _, `Excluded _ -> top () - (* Is x equal to an exclusion set, if it is a member then NO otherwise we - * don't know: *) - | `Definite x, `Excluded (s,r) -> if S.mem x s then of_bool IInt false else top () - | `Excluded (s,r), `Definite x -> if S.mem x s then of_bool IInt false else top () - (* The good case: *) - | `Definite x, `Definite y -> of_bool IInt (x = y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - (* The inequality check: *) - let ne ik x y = match x,y with - (* Not much to do with two exclusion sets: *) - | `Excluded _, `Excluded _ -> top () - (* Is x unequal to an exclusion set, if it is a member then Yes otherwise we - * don't know: *) - | `Definite x, `Excluded (s,r) -> if S.mem x s then of_bool IInt true else top () - | `Excluded (s,r), `Definite x -> if S.mem x s then of_bool IInt true else top () - (* The good case: *) - | `Definite x, `Definite y -> of_bool IInt (x <> y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - let neg ?no_ov ik (x :t) = norm ik @@ lift1 Z.neg ik x - let add ?no_ov ik x y = norm ik @@ lift2_inj Z.add ik x y - - let sub ?no_ov ik x y = norm ik @@ lift2_inj Z.sub ik x y - let mul ?no_ov ik x y = norm ik @@ match x, y with - | `Definite z, (`Excluded _ | `Definite _) when Z.equal z Z.zero -> x - | (`Excluded _ | `Definite _), `Definite z when Z.equal z Z.zero -> y - | `Definite a, `Excluded (s,r) - (* Integer multiplication with even numbers is not injective. *) - (* Thus we cannot exclude the values to which the exclusion set would be mapped to. *) - | `Excluded (s,r),`Definite a when Z.equal (Z.rem a (Z.of_int 2)) Z.zero -> `Excluded (S.empty (), apply_range (Z.mul a) r) - | _ -> lift2_inj Z.mul ik x y - let div ?no_ov ik x y = lift2 Z.div ik x y - let rem ik x y = lift2 Z.rem ik x y - - (* Comparison handling copied from Enums. *) - let handle_bot x y f = match x, y with - | `Bot, `Bot -> `Bot - | `Bot, _ - | _, `Bot -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> f () - - let lt ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 < 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 >= 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let gt ik x y = lt ik y x - - let le ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 <= 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 > 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let ge ik x y = le ik y x - - let lognot = lift1 Z.lognot - - let logand ik x y = norm ik (match x,y with - (* We don't bother with exclusion sets: *) - | `Excluded _, `Definite i -> - (* Except in two special cases *) - if Z.equal i Z.zero then - `Definite Z.zero - else if Z.equal i Z.one then - of_interval IBool (Z.zero, Z.one) - else - top () - | `Definite _, `Excluded _ - | `Excluded _, `Excluded _ -> top () - (* The good case: *) - | `Definite x, `Definite y -> - (try `Definite (Z.logand x y) with | Division_by_zero -> top ()) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) - - - let logor = lift2 Z.logor - let logxor = lift2 Z.logxor - - let shift (shift_op: int_t -> int -> int_t) (ik: Cil.ikind) (x: t) (y: t) = - (* BigInt only accepts int as second argument for shifts; perform conversion here *) - let shift_op_big_int a (b: int_t) = - let (b : int) = Z.to_int b in - shift_op a b - in - (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = GobOption.for_all (fun x -> Z.lt x Z.zero) in - if is_negative (minimal x) || is_negative (minimal y) then - top_of ik - else - norm ik @@ lift2 shift_op_big_int ik x y - - let shift_left = - shift Z.shift_left - - let shift_right = - shift Z.shift_right - (* TODO: lift does not treat Not {0} as true. *) - let c_logand ik x y = - match to_bool x, to_bool y with - | Some false, _ - | _, Some false -> - of_bool ik false - | _, _ -> - lift2 IntOps.BigIntOps.c_logand ik x y - let c_logor ik x y = - match to_bool x, to_bool y with - | Some true, _ - | _, Some true -> - of_bool ik true - | _, _ -> - lift2 IntOps.BigIntOps.c_logor ik x y - let c_lognot ik = eq ik (of_int ik Z.zero) - - let invariant_ikind e ik (x:t) = - match x with - | `Definite x -> - IntInvariant.of_int e ik x - | `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 ri = IntInvariant.of_interval e ik (rmin, rmax) in - let si = IntInvariant.of_excl_list e ik (S.elements s) in - Invariant.(ri && si) - | `Bot -> Invariant.none - - let arbitrary ik = - let open QCheck.Iter in - let excluded s = from_excl ik s in - let definite x = of_int ik x in - let shrink = function - | `Excluded (s, _) -> GobQCheck.shrink (S.arbitrary ()) s >|= excluded (* S TODO: possibly shrink excluded to definite *) - | `Definite x -> (return `Bot) <+> (GobQCheck.shrink (IntOps.BigIntOps.arbitrary ()) x >|= definite) - | `Bot -> empty - in - QCheck.frequency ~shrink ~print:show [ - 20, QCheck.map excluded (S.arbitrary ()); - 10, QCheck.map definite (IntOps.BigIntOps.arbitrary ()); - 1, QCheck.always `Bot - ] (* S TODO: decide frequencies *) - - let refine_with_congruence ik a b = a - let refine_with_interval ik a b = match a, b with - | x, Some(i) -> meet ik x (of_interval ik i) - | _ -> a - let refine_with_excl_list ik a b = match a, b with - | `Excluded (s, r), Some(ls, _) -> meet ik (`Excluded (s, r)) (of_excl_list ik ls) (* TODO: refine with excl range? *) - | _ -> a - let refine_with_incl_list ik a b = a - - let project ik p t = t -end (* Inclusion/Exclusion sets. Go to top on arithmetic operations (except for some easy cases, e.g. multiplication with 0). Joins on widen, i.e. precise integers as long as not derived from arithmetic expressions. *) module Enums : S with type int_t = Z.t = struct @@ -2742,35 +367,3 @@ module Enums : S with type int_t = Z.t = struct let project ik p t = t end - -module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct - - include D - - let lift v = (v, {overflow=false; underflow=false}) - - let add ?no_ov ik x y = lift @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = lift @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = lift @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = lift @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = lift @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = lift @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = lift @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = lift @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = lift @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = lift @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = lift @@ D.shift_left ik x y - - let shift_right ik x y = lift @@ D.shift_right ik x y - -end From 94581ee6820d7c0668d99c9f4447b1d190eb8426 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:00:28 +0300 Subject: [PATCH 466/566] Remove Enums from IntDomain0 --- src/cdomain/value/cdomains/intDomain0.ml | 367 ----------------------- 1 file changed, 367 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/intDomain0.ml index 8dd8b07f74..1cda533c55 100644 --- a/src/cdomain/value/cdomains/intDomain0.ml +++ b/src/cdomain/value/cdomains/intDomain0.ml @@ -2376,373 +2376,6 @@ struct let project ik p t = t end -(* Inclusion/Exclusion sets. Go to top on arithmetic operations (except for some easy cases, e.g. multiplication with 0). Joins on widen, i.e. precise integers as long as not derived from arithmetic expressions. *) -module Enums : S with type int_t = Z.t = struct - module R = Interval32 (* range for exclusion *) - - let range_ikind = Cil.IInt - let size t = R.of_interval range_ikind (let a,b = Size.bits_i64 t in Int64.neg a,b) - - type t = Inc of BISet.t | Exc of BISet.t * R.t [@@deriving eq, ord, hash] (* inclusion/exclusion set *) - - type int_t = Z.t - let name () = "enums" - let bot () = failwith "bot () not implemented for Enums" - let top () = failwith "top () not implemented for Enums" - let bot_of ik = Inc (BISet.empty ()) - let top_bool = Inc (BISet.of_list [Z.zero; Z.one]) - let top_of ik = - match ik with - | IBool -> top_bool - | _ -> Exc (BISet.empty (), size ik) - - let range ik = Size.range ik - -(* - let max_of_range r = Size.max_from_bit_range (Option.get (R.maximal r)) - let min_of_range r = Size.min_from_bit_range (Option.get (R.minimal r)) - let cardinality_of_range r = Z.add (Z.neg (min_of_range r)) (max_of_range r) *) - let value_in_range (min, max) v = Z.compare min v <= 0 && Z.compare v max <= 0 - - let show = function - | Inc xs when BISet.is_empty xs -> "bot" - | Inc xs -> "{" ^ (String.concat ", " (List.map Z.to_string (BISet.elements xs))) ^ "}" - | Exc (xs,r) -> "not {" ^ (String.concat ", " (List.map Z.to_string (BISet.elements xs))) ^ "} " ^ "("^R.show r^")" - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - (* Normalization function for enums, that handles overflows for Inc. - As we do not compute on Excl, we do not have to perform any overflow handling for it. *) - let norm ikind v = - let min, max = range ikind in - (* Whether the value v lies within the values of the specified ikind. *) - let value_in_ikind v = - Z.compare min v <= 0 && Z.compare v max <= 0 - in - match v with - | Inc xs when BISet.for_all value_in_ikind xs -> v - | Inc xs -> - if should_wrap ikind then - Inc (BISet.map (Size.cast ikind) xs) - else if should_ignore_overflow ikind then - Inc (BISet.filter value_in_ikind xs) - else - top_of ikind - | Exc (xs, r) -> - (* The following assert should hold for Exc, therefore we do not have to overflow handling / normalization for it: - let range_in_ikind r = - R.leq r (size ikind) - in - let r_min, r_max = min_of_range r, max_of_range r in - assert (range_in_ikind r && BISet.for_all (value_in_range (r_min, r_max)) xs); *) - begin match ikind with - | IBool -> - begin match BISet.mem Z.zero xs, BISet.mem Z.one xs with - | false, false -> top_bool (* Not {} -> {0, 1} *) - | true, false -> Inc (BISet.singleton Z.one) (* Not {0} -> {1} *) - | false, true -> Inc (BISet.singleton Z.zero) (* Not {1} -> {0} *) - | true, true -> bot_of ikind (* Not {0, 1} -> bot *) - end - | _ -> - v - end - - - let equal_to i = function - | Inc x -> - if BISet.mem i x then - if BISet.is_singleton x then `Eq - else `Top - else `Neq - | Exc (x, r) -> - if BISet.mem i x then `Neq - else `Top - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik v = norm ik @@ match v with - | Exc (s,r) -> - let r' = size ik in - if R.leq r r' then (* upcast -> no change *) - Exc (s, r) - else if ik = IBool then (* downcast to bool *) - if BISet.mem Z.zero s then - Inc (BISet.singleton Z.one) - else - Exc (BISet.empty(), r') - else (* downcast: may overflow *) - Exc ((BISet.empty ()), r') - | Inc xs -> - let casted_xs = BISet.map (Size.cast ik) xs in - if Cil.isSigned ik && not (BISet.equal xs casted_xs) - then top_of ik (* When casting into a signed type and the result does not fit, the behavior is implementation-defined *) - else Inc casted_xs - - let of_int ikind x = cast_to ikind (Inc (BISet.singleton x)) - - let of_interval ?(suppress_ovwarn=false) ik (x, y) = - if Z.compare x y = 0 then - of_int ik x - else - let a, b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval ~suppress_ovwarn range_ikind a) (R.of_interval ~suppress_ovwarn range_ikind b) in - let ex = if Z.gt x Z.zero || Z.lt y Z.zero then BISet.singleton Z.zero else BISet.empty () in - norm ik @@ (Exc (ex, r)) - - let join _ x y = - match x, y with - | Inc x, Inc y -> Inc (BISet.union x y) - | Exc (x,r1), Exc (y,r2) -> Exc (BISet.inter x y, R.join r1 r2) - | Exc (x,r), Inc y - | Inc y, Exc (x,r) -> - let r = if BISet.is_empty y - then r - else - let (min_el_range, max_el_range) = Batteries.Tuple2.mapn (fun x -> R.of_interval range_ikind (Size.min_range_sign_agnostic x)) (BISet.min_elt y, BISet.max_elt y) in - let range = R.join min_el_range max_el_range in - R.join r range - in - Exc (BISet.diff x y, r) - - let meet _ x y = - match x, y with - | Inc x, Inc y -> Inc (BISet.inter x y) - | Exc (x,r1), Exc (y,r2) -> - let r = R.meet r1 r2 in - let r_min, r_max = Exclusion.min_of_range r, Exclusion.max_of_range r in - let filter_by_range = BISet.filter (value_in_range (r_min, r_max)) in - (* We remove those elements from the exclusion set that do not fit in the range anyway *) - let excl = BISet.union (filter_by_range x) (filter_by_range y) in - Exc (excl, r) - | Inc x, Exc (y,r) - | Exc (y,r), Inc x -> Inc (BISet.diff x y) - - let widen = join - let narrow = meet - let leq a b = - match a, b with - | Inc xs, Exc (ys, r) -> - if BISet.is_empty xs - then true - else - let min_b, max_b = Exclusion.min_of_range r, Exclusion.max_of_range r in - let min_a, max_a = BISet.min_elt xs, BISet.max_elt xs in - (* Check that the xs fit into the range r *) - Z.compare min_b min_a <= 0 && Z.compare max_a max_b <= 0 && - (* && check that none of the values contained in xs is excluded, i.e. contained in ys. *) - BISet.for_all (fun x -> not (BISet.mem x ys)) xs - | Inc xs, Inc ys -> - BISet.subset xs ys - | Exc (xs, r), Exc (ys, s) -> - Exclusion.(leq (Exc (xs, r)) (Exc (ys, s))) - | Exc (xs, r), Inc ys -> - Exclusion.(leq_excl_incl (Exc (xs, r)) (Inc ys)) - - let handle_bot x y f = match is_bot x, is_bot y with - | false, false -> f () - | true, false - | false, true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | true, true -> Inc (BISet.empty ()) - - let lift1 f ikind v = norm ikind @@ match v with - | Inc x when BISet.is_empty x -> v (* Return bottom when value is bottom *) - | Inc x when BISet.is_singleton x -> Inc (BISet.singleton (f (BISet.choose x))) - | _ -> top_of ikind - - let lift2 f (ikind: Cil.ikind) u v = - handle_bot u v (fun () -> - norm ikind @@ match u, v with - | Inc x,Inc y when BISet.is_singleton x && BISet.is_singleton y -> Inc (BISet.singleton (f (BISet.choose x) (BISet.choose y))) - | _,_ -> top_of ikind) - - let lift2 f ikind a b = - try lift2 f ikind a b with Division_by_zero -> top_of ikind - - let neg ?no_ov = lift1 Z.neg - let add ?no_ov ikind a b = - match a, b with - | Inc z,x when BISet.is_singleton z && BISet.choose z = Z.zero -> x - | x,Inc z when BISet.is_singleton z && BISet.choose z = Z.zero -> x - | x,y -> lift2 Z.add ikind x y - let sub ?no_ov = lift2 Z.sub - let mul ?no_ov ikind a b = - match a, b with - | Inc one,x when BISet.is_singleton one && BISet.choose one = Z.one -> x - | x,Inc one when BISet.is_singleton one && BISet.choose one = Z.one -> x - | Inc zero,_ when BISet.is_singleton zero && BISet.choose zero = Z.zero -> a - | _,Inc zero when BISet.is_singleton zero && BISet.choose zero = Z.zero -> b - | x,y -> lift2 Z.mul ikind x y - - let div ?no_ov ikind a b = match a, b with - | x,Inc one when BISet.is_singleton one && BISet.choose one = Z.one -> x - | _,Inc zero when BISet.is_singleton zero && BISet.choose zero = Z.zero -> top_of ikind - | Inc zero,_ when BISet.is_singleton zero && BISet.choose zero = Z.zero -> a - | x,y -> lift2 Z.div ikind x y - - let rem = lift2 Z.rem - - let lognot = lift1 Z.lognot - let logand = lift2 Z.logand - let logor = lift2 Z.logor - let logxor = lift2 Z.logxor - - let shift (shift_op: int_t -> int -> int_t) (ik: Cil.ikind) (x: t) (y: t) = - handle_bot x y (fun () -> - (* BigInt only accepts int as second argument for shifts; perform conversion here *) - let shift_op_big_int a (b: int_t) = - let (b : int) = Z.to_int b in - shift_op a b - in - (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = GobOption.for_all (fun x -> Z.lt x Z.zero) in - if is_negative (minimal x) || is_negative (minimal y) then - top_of ik - else - lift2 shift_op_big_int ik x y) - - let shift_left = - shift Z.shift_left - - let shift_right = - shift Z.shift_right - - let of_bool ikind x = Inc (BISet.singleton (if x then Z.one else Z.zero)) - let to_bool = function - | Inc e when BISet.is_empty e -> None - | Exc (e,_) when BISet.is_empty e -> None - | Inc zero when BISet.is_singleton zero && BISet.choose zero = Z.zero -> Some false - | Inc xs when BISet.for_all ((<>) Z.zero) xs -> Some true - | Exc (xs,_) when BISet.exists ((=) Z.zero) xs -> Some true - | _ -> None - let to_int = function Inc x when BISet.is_singleton x -> Some (BISet.choose x) | _ -> None - - let to_excl_list = function Exc (x,r) when not (BISet.is_empty x) -> Some (BISet.elements x, (Option.get (R.minimal r), Option.get (R.maximal r))) | _ -> None - let of_excl_list ik xs = - let min_ik, max_ik = Size.range ik in - let exc = BISet.of_list @@ List.filter (value_in_range (min_ik, max_ik)) xs in - norm ik @@ Exc (exc, size ik) - let is_excl_list = BatOption.is_some % to_excl_list - let to_incl_list = function Inc s when not (BISet.is_empty s) -> Some (BISet.elements s) | _ -> None - - let starting ?(suppress_ovwarn=false) ikind x = - let _,u_ik = Size.range ikind in - of_interval ~suppress_ovwarn ikind (x, u_ik) - - let ending ?(suppress_ovwarn=false) ikind x = - let l_ik,_ = Size.range ikind in - of_interval ~suppress_ovwarn ikind (l_ik, x) - - let c_lognot ik x = - if is_bot x - then x - else - match to_bool x with - | Some b -> of_bool ik (not b) - | None -> top_bool - - let c_logand = lift2 IntOps.BigIntOps.c_logand - let c_logor = lift2 IntOps.BigIntOps.c_logor - let maximal = function - | Inc xs when not (BISet.is_empty xs) -> Some (BISet.max_elt xs) - | Exc (excl,r) -> - let rec decrement_while_contained v = - if BISet.mem v excl - then decrement_while_contained (Z.pred v) - else v - in - let range_max = Exclusion.max_of_range r in - Some (decrement_while_contained range_max) - | _ (* bottom case *) -> None - - let minimal = function - | Inc xs when not (BISet.is_empty xs) -> Some (BISet.min_elt xs) - | Exc (excl,r) -> - let rec increment_while_contained v = - if BISet.mem v excl - then increment_while_contained (Z.succ v) - else v - in - let range_min = Exclusion.min_of_range r in - Some (increment_while_contained range_min) - | _ (* bottom case *) -> None - - let lt ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 < 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 >= 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let gt ik x y = lt ik y x - - let le ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 <= 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 > 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let ge ik x y = le ik y x - - let eq ik x y = - handle_bot x y (fun () -> - match x, y with - | Inc xs, Inc ys when BISet.is_singleton xs && BISet.is_singleton ys -> of_bool ik (Z.equal (BISet.choose xs) (BISet.choose ys)) - | _, _ -> - if is_bot (meet ik x y) then - (* If the meet is empty, there is no chance that concrete values are equal *) - of_bool ik false - else - top_bool) - - let ne ik x y = c_lognot ik (eq ik x y) - - let invariant_ikind e ik x = - match x with - | Inc ps -> - IntInvariant.of_incl_list e ik (BISet.elements ps) - | Exc (ns, 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 ri = IntInvariant.of_interval e ik (rmin, rmax) in - let nsi = IntInvariant.of_excl_list e ik (BISet.elements ns) in - Invariant.(ri && nsi) - - - let arbitrary ik = - let open QCheck.Iter in - let neg s = of_excl_list ik (BISet.elements s) in - let pos s = norm ik (Inc s) in - let shrink = function - | Exc (s, _) -> GobQCheck.shrink (BISet.arbitrary ()) s >|= neg (* S TODO: possibly shrink neg to pos *) - | Inc s -> GobQCheck.shrink (BISet.arbitrary ()) s >|= pos - in - QCheck.frequency ~shrink ~print:show [ - 20, QCheck.map neg (BISet.arbitrary ()); - 10, QCheck.map pos (BISet.arbitrary ()); - ] (* S TODO: decide frequencies *) - - let refine_with_congruence ik a b = - let contains c m x = if Z.equal m Z.zero then Z.equal c x else Z.equal (Z.rem (Z.sub x c) m) Z.zero in - match a, b with - | Inc e, None -> bot_of ik - | Inc e, Some (c, m) -> Inc (BISet.filter (contains c m) e) - | _ -> a - - let refine_with_interval ik a b = a (* TODO: refine inclusion (exclusion?) set *) - - let refine_with_excl_list ik a b = - match b with - | Some (ls, _) -> meet ik a (of_excl_list ik ls) (* TODO: refine with excl range? *) - | _ -> a - - let refine_with_incl_list ik a b = - match a, b with - | Inc x, Some (ls) -> meet ik (Inc x) (Inc (BISet.of_list ls)) - | _ -> a - - let project ik p t = t -end - module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct include D From c4a3876e35c4d357f6abb4a91a1cc725326b0fe7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:02:32 +0300 Subject: [PATCH 467/566] Rename IntDomain0 -> DefExcDomain for split --- src/cdomain/value/cdomains/{intDomain0.ml => int/defExcDomain.ml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/cdomain/value/cdomains/{intDomain0.ml => int/defExcDomain.ml} (100%) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/int/defExcDomain.ml similarity index 100% rename from src/cdomain/value/cdomains/intDomain0.ml rename to src/cdomain/value/cdomains/int/defExcDomain.ml From 108ab4410310978efe596c5a9bf922d17ab8b7e6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:02:59 +0300 Subject: [PATCH 468/566] Remove non-DefExcDomain parts --- .../value/cdomains/int/defExcDomain.ml | 1930 +---------------- 1 file changed, 1 insertion(+), 1929 deletions(-) diff --git a/src/cdomain/value/cdomains/int/defExcDomain.ml b/src/cdomain/value/cdomains/int/defExcDomain.ml index 1cda533c55..e747176631 100644 --- a/src/cdomain/value/cdomains/int/defExcDomain.ml +++ b/src/cdomain/value/cdomains/int/defExcDomain.ml @@ -1,1901 +1,5 @@ -open GobConfig -open GoblintCil -open Pretty -open PrecisionUtil +open IntDomain0 -module M = Messages - -let (%) = Batteries.(%) -let (|?) = Batteries.(|?) - -exception IncompatibleIKinds of string -exception Unknown -exception Error -exception ArithmeticOnIntegerBot of string - - - - -(** Define records that hold mutable variables representing different Configuration values. - * These values are used to keep track of whether or not the corresponding Config values are en-/disabled *) -type ana_int_config_values = { - mutable interval_threshold_widening : bool option; - mutable interval_narrow_by_meet : bool option; - mutable def_exc_widen_by_join : bool option; - mutable interval_threshold_widening_constants : string option; - mutable refinement : string option; -} - -let ana_int_config: ana_int_config_values = { - interval_threshold_widening = None; - interval_narrow_by_meet = None; - def_exc_widen_by_join = None; - interval_threshold_widening_constants = None; - refinement = None; -} - -let get_interval_threshold_widening () = - if ana_int_config.interval_threshold_widening = None then - ana_int_config.interval_threshold_widening <- Some (get_bool "ana.int.interval_threshold_widening"); - Option.get ana_int_config.interval_threshold_widening - -let get_interval_narrow_by_meet () = - if ana_int_config.interval_narrow_by_meet = None then - ana_int_config.interval_narrow_by_meet <- Some (get_bool "ana.int.interval_narrow_by_meet"); - Option.get ana_int_config.interval_narrow_by_meet - -let get_def_exc_widen_by_join () = - if ana_int_config.def_exc_widen_by_join = None then - ana_int_config.def_exc_widen_by_join <- Some (get_bool "ana.int.def_exc_widen_by_join"); - Option.get ana_int_config.def_exc_widen_by_join - -let get_interval_threshold_widening_constants () = - if ana_int_config.interval_threshold_widening_constants = None then - ana_int_config.interval_threshold_widening_constants <- Some (get_string "ana.int.interval_threshold_widening_constants"); - Option.get ana_int_config.interval_threshold_widening_constants - -let get_refinement () = - if ana_int_config.refinement = None then - ana_int_config.refinement <- Some (get_string "ana.int.refinement"); - Option.get ana_int_config.refinement - - - -(** Whether for a given ikind, we should compute with wrap-around arithmetic. - * Always for unsigned types, for signed types if 'sem.int.signed_overflow' is 'assume_wraparound' *) -let should_wrap ik = not (Cil.isSigned ik) || get_string "sem.int.signed_overflow" = "assume_wraparound" - -(** Whether for a given ikind, we should assume there are no overflows. - * Always false for unsigned types, true for signed types if 'sem.int.signed_overflow' is 'assume_none' *) -let should_ignore_overflow ik = Cil.isSigned ik && get_string "sem.int.signed_overflow" = "assume_none" - -let widening_thresholds = ResettableLazy.from_fun WideningThresholds.thresholds -let widening_thresholds_desc = ResettableLazy.from_fun (List.rev % WideningThresholds.thresholds) - -type overflow_info = { overflow: bool; underflow: bool;} - -let set_overflow_flag ~cast ~underflow ~overflow ik = - if !AnalysisState.executing_speculative_computations then - (* Do not produce warnings when the operations are not actually happening in code *) - () - else - let signed = Cil.isSigned ik in - if !AnalysisState.postsolving && signed && not cast then - AnalysisState.svcomp_may_overflow := true; - let sign = if signed then "Signed" else "Unsigned" in - match underflow, overflow with - | true, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190; CWE 191] "%s integer overflow and underflow" sign - | true, false -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 191] "%s integer underflow" sign - | false, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190] "%s integer overflow" sign - | false, false -> assert false - -let reset_lazy () = - ResettableLazy.reset widening_thresholds; - ResettableLazy.reset widening_thresholds_desc; - ana_int_config.interval_threshold_widening <- None; - ana_int_config.interval_narrow_by_meet <- None; - ana_int_config.def_exc_widen_by_join <- None; - ana_int_config.interval_threshold_widening_constants <- None; - ana_int_config.refinement <- None - -module type Arith = -sig - type t - val neg: t -> t - val add: t -> t -> t - val sub: t -> t -> t - val mul: t -> t -> t - val div: t -> t -> t - val rem: t -> t -> t - - val lt: t -> t -> t - val gt: t -> t -> t - val le: t -> t -> t - val ge: t -> t -> t - val eq: t -> t -> t - val ne: t -> t -> t - - val lognot: t -> t - val logand: t -> t -> t - val logor : t -> t -> t - val logxor: t -> t -> t - - val shift_left : t -> t -> t - val shift_right: t -> t -> t - - val c_lognot: t -> t - val c_logand: t -> t -> t - val c_logor : t -> t -> t - -end - -module type ArithIkind = -sig - type t - val neg: Cil.ikind -> t -> t - val add: Cil.ikind -> t -> t -> t - val sub: Cil.ikind -> t -> t -> t - val mul: Cil.ikind -> t -> t -> t - val div: Cil.ikind -> t -> t -> t - val rem: Cil.ikind -> t -> t -> t - - val lt: Cil.ikind -> t -> t -> t - val gt: Cil.ikind -> t -> t -> t - val le: Cil.ikind -> t -> t -> t - val ge: Cil.ikind -> t -> t -> t - val eq: Cil.ikind -> t -> t -> t - val ne: Cil.ikind -> t -> t -> t - - val lognot: Cil.ikind -> t -> t - val logand: Cil.ikind -> t -> t -> t - val logor : Cil.ikind -> t -> t -> t - val logxor: Cil.ikind -> t -> t -> t - - val shift_left : Cil.ikind -> t -> t -> t - val shift_right: Cil.ikind -> t -> t -> t - - val c_lognot: Cil.ikind -> t -> t - val c_logand: Cil.ikind -> t -> t -> t - val c_logor : Cil.ikind -> t -> t -> t - -end - -(* Shared functions between S and Z *) -module type B = -sig - include Lattice.S - type int_t - val bot_of: Cil.ikind -> t - val top_of: Cil.ikind -> t - val to_int: t -> int_t option - val equal_to: int_t -> t -> [`Eq | `Neq | `Top] - - val to_bool: t -> bool option - val to_excl_list: t -> (int_t list * (int64 * int64)) option - val of_excl_list: Cil.ikind -> int_t list -> t - val is_excl_list: t -> bool - - val to_incl_list: t -> int_t list option - - val maximal : t -> int_t option - val minimal : t -> int_t option - - val cast_to: ?suppress_ovwarn:bool -> ?torg:Cil.typ -> Cil.ikind -> t -> t -end - -(** Interface of IntDomain implementations that do not take ikinds for arithmetic operations yet. TODO: Should be ported to S in the future. *) -module type IkindUnawareS = -sig - include B - include Arith with type t := t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: int_t -> t - val of_bool: bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val arbitrary: unit -> t QCheck.arbitrary - val invariant: Cil.exp -> t -> Invariant.t -end - -(** Interface of IntDomain implementations taking an ikind for arithmetic operations *) -module type S = -sig - include B - include ArithIkind with type t:= t - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val neg : ?no_ov:bool -> Cil.ikind -> t -> t - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t - - val join: Cil.ikind -> t -> t -> t - val meet: Cil.ikind -> t -> t -> t - val narrow: Cil.ikind -> t -> t -> t - val widen: Cil.ikind -> t -> t -> t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val is_top_of: Cil.ikind -> t -> bool - val invariant_ikind : Cil.exp -> Cil.ikind -> t -> Invariant.t - - val refine_with_congruence: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_interval: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_excl_list: Cil.ikind -> t -> (int_t list * (int64 * int64)) option -> t - val refine_with_incl_list: Cil.ikind -> t -> int_t list option -> t - - val project: Cil.ikind -> int_precision -> t -> t - val arbitrary: Cil.ikind -> t QCheck.arbitrary -end - -module type SOverflow = -sig - - include S - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val neg : ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val of_int : Cil.ikind -> int_t -> t * overflow_info - - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t * overflow_info - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - - val shift_left : Cil.ikind -> t -> t -> t * overflow_info - - val shift_right : Cil.ikind -> t -> t -> t * overflow_info -end - -module type Y = -sig - (* include B *) - include B - include Arith with type t:= t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val is_top_of: Cil.ikind -> t -> bool - - val project: int_precision -> t -> t - val invariant: Cil.exp -> t -> Invariant.t -end - -module type Z = Y with type int_t = Z.t - - -module IntDomLifter (I : S) = -struct - open Cil - 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 (GobPretty.sprintf "ikinds %a and %a are incompatible. Values: %a and %a" CilType.Ikind.pretty x.ikind CilType.Ikind.pretty y.ikind I.pretty x.v I.pretty y.v)) - let lift op x = {x with v = op x.ikind x.v } - (* For logical operations the result is of type int *) - let lift_logical op x = {v = op x.ikind x.v; ikind = Cil.IInt} - let lift2 op x y = check_ikinds x y; {x with v = op x.ikind x.v y.v } - let lift2_cmp op x y = check_ikinds x y; {v = op x.ikind x.v y.v; ikind = Cil.IInt} - - let bot_of ikind = { v = I.bot_of ikind; ikind} - let bot () = failwith "bot () is not implemented for IntDomLifter." - let is_bot x = I.is_bot x.v - let top_of ikind = { v = I.top_of ikind; ikind} - let top () = failwith "top () is not implemented for IntDomLifter." - let is_top x = I.is_top x.v - - (* Leq does not check for ikind, because it is used in invariant with arguments of different type. - TODO: check ikinds here and fix invariant to work with right ikinds *) - let leq x y = I.leq x.v y.v - let join = lift2 I.join - let meet = lift2 I.meet - let widen = lift2 I.widen - let narrow = lift2 I.narrow - - let show x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - "⊤" - else - I.show x.v (* TODO add ikind to output *) - let pretty () x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - Pretty.text "⊤" - else - I.pretty () x.v (* TODO add ikind to output *) - let pretty_diff () (x, y) = I.pretty_diff () (x.v, y.v) (* TODO check ikinds, add them to output *) - let printXml o x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - BatPrintf.fprintf o "\n\n⊤\n\n\n" - else - I.printXml o x.v (* TODO add ikind to output *) - (* This is for debugging *) - let name () = "IntDomLifter(" ^ (I.name ()) ^ ")" - let to_yojson x = I.to_yojson x.v - let invariant e x = - let e' = Cilfacade.mkCast ~e ~newt:(TInt (x.ikind, [])) in - I.invariant_ikind e' x.ikind x.v - let tag x = I.tag x.v - let arbitrary ik = failwith @@ "Arbitrary not implement for " ^ (name ()) ^ "." - let to_int x = I.to_int x.v - let of_int ikind x = { v = I.of_int ikind x; ikind} - let equal_to i x = I.equal_to i x.v - let to_bool x = I.to_bool x.v - let of_bool ikind b = { v = I.of_bool ikind b; ikind} - let to_excl_list x = I.to_excl_list x.v - let of_excl_list ikind is = {v = I.of_excl_list ikind is; ikind} - let is_excl_list x = I.is_excl_list x.v - let to_incl_list x = I.to_incl_list x.v - let of_interval ?(suppress_ovwarn=false) ikind (lb,ub) = {v = I.of_interval ~suppress_ovwarn ikind (lb,ub); ikind} - let of_congruence ikind (c,m) = {v = I.of_congruence ikind (c,m); ikind} - let starting ?(suppress_ovwarn=false) ikind i = {v = I.starting ~suppress_ovwarn ikind i; ikind} - let ending ?(suppress_ovwarn=false) ikind i = {v = I.ending ~suppress_ovwarn ikind i; ikind} - let maximal x = I.maximal x.v - let minimal x = I.minimal x.v - - let neg = lift I.neg - let add = lift2 I.add - let sub = lift2 I.sub - let mul = lift2 I.mul - let div = lift2 I.div - let rem = lift2 I.rem - let lt = lift2_cmp I.lt - let gt = lift2_cmp I.gt - let le = lift2_cmp I.le - let ge = lift2_cmp I.ge - let eq = lift2_cmp I.eq - let ne = lift2_cmp I.ne - let lognot = lift I.lognot - let logand = lift2 I.logand - let logor = lift2 I.logor - let logxor = lift2 I.logxor - let shift_left x y = {x with v = I.shift_left x.ikind x.v y.v } (* TODO check ikinds*) - let shift_right x y = {x with v = I.shift_right x.ikind x.v y.v } (* TODO check ikinds*) - let c_lognot = lift_logical I.c_lognot - let c_logand = lift2 I.c_logand - let c_logor = lift2 I.c_logor - - let cast_to ?(suppress_ovwarn=false) ?torg ikind x = {v = I.cast_to ~suppress_ovwarn ~torg:(TInt(x.ikind,[])) ikind x.v; ikind} - - let is_top_of ik x = ik = x.ikind && I.is_top_of ik x.v - - let relift x = { v = I.relift x.v; ikind = x.ikind } - - let project p v = { v = I.project v.ikind p v.v; ikind = v.ikind } -end - -module type Ikind = -sig - val ikind: unit -> Cil.ikind -end - -module PtrDiffIkind : Ikind = -struct - let ikind = Cilfacade.ptrdiff_ikind -end - -module IntDomWithDefaultIkind (I: Y) (Ik: Ikind) : Y with type t = I.t and type int_t = I.int_t = -struct - include I - let top () = I.top_of (Ik.ikind ()) - let bot () = I.bot_of (Ik.ikind ()) -end - -module Size = struct (* size in bits as int, range as int64 *) - open Cil - let sign x = if Z.compare x Z.zero < 0 then `Signed else `Unsigned - - let top_typ = TInt (ILongLong, []) - let min_for x = intKindForValue x (sign x = `Unsigned) - let bit = function (* bits needed for representation *) - | IBool -> 1 - | ik -> bytesSizeOfInt ik * 8 - let is_int64_big_int x = Z.fits_int64 x - let card ik = (* cardinality *) - let b = bit ik in - Z.shift_left Z.one b - let bits ik = (* highest bits for neg/pos values *) - let s = bit ik in - if isSigned ik then s-1, s-1 else 0, s - let bits_i64 ik = BatTuple.Tuple2.mapn Int64.of_int (bits ik) - let range ik = - let a,b = bits ik in - let x = if isSigned ik then Z.neg (Z.shift_left Z.one a) (* -2^a *) else Z.zero in - let y = Z.pred (Z.shift_left Z.one b) in (* 2^b - 1 *) - x,y - - let is_cast_injective ~from_type ~to_type = - let (from_min, from_max) = range (Cilfacade.get_ikind from_type) in - let (to_min, to_max) = range (Cilfacade.get_ikind to_type) in - if M.tracing then M.trace "int" "is_cast_injective %a (%a, %a) -> %a (%a, %a)" CilType.Typ.pretty from_type GobZ.pretty from_min GobZ.pretty from_max CilType.Typ.pretty to_type GobZ.pretty to_min GobZ.pretty to_max; - Z.compare to_min from_min <= 0 && Z.compare from_max to_max <= 0 - - let cast t x = (* TODO: overflow is implementation-dependent! *) - if t = IBool then - (* C11 6.3.1.2 Boolean type *) - if Z.equal x Z.zero then Z.zero else Z.one - else - let a,b = range t in - let c = card t in - let y = Z.erem x c in - let y = if Z.gt y b then Z.sub y c - else if Z.lt y a then Z.add y c - else y - in - if M.tracing then M.tracel "cast" "Cast %a to range [%a, %a] (%a) = %a (%s in int64)" GobZ.pretty x GobZ.pretty a GobZ.pretty b GobZ.pretty c GobZ.pretty y (if is_int64_big_int y then "fits" else "does not fit"); - y - - let min_range_sign_agnostic x = - let size ik = - let a,b = bits_i64 ik in - Int64.neg a,b - in - if sign x = `Signed then - size (min_for x) - else - let a, b = size (min_for x) in - if b <= 64L then - let upper_bound_less = Int64.sub b 1L in - let max_one_less = Z.(pred @@ shift_left Z.one (Int64.to_int upper_bound_less)) in - if x <= max_one_less then - a, upper_bound_less - else - a,b - else - a, b - - (* From the number of bits used to represent a positive value, determines the maximal representable value *) - let max_from_bit_range pos_bits = Z.(pred @@ shift_left Z.one (to_int (Z.of_int64 pos_bits))) - - (* From the number of bits used to represent a non-positive value, determines the minimal representable value *) - let min_from_bit_range neg_bits = Z.(if neg_bits = 0L then Z.zero else neg @@ shift_left Z.one (to_int (neg (Z.of_int64 neg_bits)))) - -end - - -module StdTop (B: sig type t val top_of: Cil.ikind -> t end) = struct - open B - (* these should be overwritten for better precision if possible: *) - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ik x = top_of ik - let ending ?(suppress_ovwarn=false) ik x = top_of ik - let maximal x = None - let minimal x = None -end - -module Std (B: sig - type t - val name: unit -> string - val top_of: Cil.ikind -> t - val bot_of: Cil.ikind -> t - val show: t -> string - val equal: t -> t -> bool - end) = struct - include Printable.StdLeaf - let name = B.name (* overwrite the one from Printable.Std *) - open B - let is_top x = failwith "is_top not implemented for IntDomain.Std" - let is_bot x = B.equal x (bot_of Cil.IInt) (* Here we assume that the representation of bottom is independent of the ikind - This may be true for intdomain implementations, but not e.g. for IntDomLifter. *) - let is_top_of ik x = B.equal x (top_of ik) - - (* all output is based on B.show *) - include Printable.SimpleShow ( - struct - type nonrec t = t - let show = show - end - ) - let pretty_diff () (x,y) = dprintf "%s: %a instead of %a" (name ()) pretty x pretty y - - include StdTop (B) -end - -(* Textbook interval arithmetic, without any overflow handling etc. *) -module IntervalArith (Ints_t : IntOps.IntOps) = struct - let min4 a b c d = Ints_t.min (Ints_t.min a b) (Ints_t.min c d) - let max4 a b c d = Ints_t.max (Ints_t.max a b) (Ints_t.max c d) - - let mul (x1, x2) (y1, y2) = - let x1y1 = (Ints_t.mul x1 y1) in - let x1y2 = (Ints_t.mul x1 y2) in - let x2y1 = (Ints_t.mul x2 y1) in - let x2y2 = (Ints_t.mul x2 y2) in - (min4 x1y1 x1y2 x2y1 x2y2, max4 x1y1 x1y2 x2y1 x2y2) - - let shift_left (x1,x2) (y1,y2) = - let y1p = Ints_t.shift_left Ints_t.one y1 in - let y2p = Ints_t.shift_left Ints_t.one y2 in - mul (x1, x2) (y1p, y2p) - - let div (x1, x2) (y1, y2) = - let x1y1n = (Ints_t.div x1 y1) in - let x1y2n = (Ints_t.div x1 y2) in - let x2y1n = (Ints_t.div x2 y1) in - let x2y2n = (Ints_t.div x2 y2) in - let x1y1p = (Ints_t.div x1 y1) in - let x1y2p = (Ints_t.div x1 y2) in - let x2y1p = (Ints_t.div x2 y1) in - let x2y2p = (Ints_t.div x2 y2) in - (min4 x1y1n x1y2n x2y1n x2y2n, max4 x1y1p x1y2p x2y1p x2y2p) - - let add (x1, x2) (y1, y2) = (Ints_t.add x1 y1, Ints_t.add x2 y2) - let sub (x1, x2) (y1, y2) = (Ints_t.sub x1 y2, Ints_t.sub x2 y1) - - let neg (x1, x2) = (Ints_t.neg x2, Ints_t.neg x1) - - let one = (Ints_t.one, Ints_t.one) - let zero = (Ints_t.zero, Ints_t.zero) - let top_bool = (Ints_t.zero, Ints_t.one) - - let to_int (x1, x2) = - if Ints_t.equal x1 x2 then Some x1 else None - - let upper_threshold u max_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - let max_ik' = Ints_t.to_bigint max_ik in - let t = List.find_opt (fun x -> Z.compare u x <= 0 && Z.compare x max_ik' <= 0) ts in - BatOption.map_default Ints_t.of_bigint max_ik t - let lower_threshold l min_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - let min_ik' = Ints_t.to_bigint min_ik in - let t = List.find_opt (fun x -> Z.compare l x >= 0 && Z.compare x min_ik' >= 0) ts in - BatOption.map_default Ints_t.of_bigint min_ik t - let is_upper_threshold u = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - List.exists (Z.equal u) ts - let is_lower_threshold l = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - List.exists (Z.equal l) ts -end - -module IntInvariant = -struct - let of_int e ik x = - if get_bool "witness.invariant.exact" then - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) - else - Invariant.none - - let of_incl_list e ik ps = - match ps with - | [_; _] when ik = IBool && not (get_bool "witness.invariant.inexact-type-bounds") -> - assert (List.mem Z.zero ps); - assert (List.mem Z.one ps); - Invariant.none - | [_] when get_bool "witness.invariant.exact" -> - Invariant.none - | _ :: _ :: _ - | [_] | [] -> - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in - Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) (Invariant.bot ()) ps - - let of_interval_opt e ik = function - | (Some x1, Some x2) when Z.equal x1 x2 -> - of_int e ik x1 - | x1_opt, x2_opt -> - let (min_ik, max_ik) = Size.range ik in - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in - let i1 = - match x1_opt, inexact_type_bounds with - | Some x1, false when Z.equal min_ik x1 -> Invariant.none - | Some x1, _ -> Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1, e, intType)) - | None, _ -> Invariant.none - in - let i2 = - match x2_opt, inexact_type_bounds with - | Some x2, false when Z.equal x2 max_ik -> Invariant.none - | Some x2, _ -> Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) - | None, _ -> Invariant.none - in - Invariant.(i1 && i2) - - let of_interval e ik (x1, x2) = - of_interval_opt e ik (Some x1, Some x2) - - let of_excl_list e ik ns = - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in - Invariant.(a && i) - ) (Invariant.top ()) ns -end - -module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = -struct - let name () = "intervals" - type int_t = Ints_t.t - type t = (Ints_t.t * Ints_t.t) option [@@deriving eq, ord, hash] - module IArith = IntervalArith (Ints_t) - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - let top_of ik = Some (range ik) - let bot () = None - let bot_of ik = bot () (* TODO: improve *) - - let show = function None -> "bottom" | Some (x,y) -> "["^Ints_t.to_string x^","^Ints_t.to_string y^"]" - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let equal_to i = function - | None -> failwith "unsupported: equal_to with bottom" - | Some (a, b) -> - if a = b && b = i then `Eq else if Ints_t.compare a i <= 0 && Ints_t.compare i b <=0 then `Top else `Neq - - let norm ?(suppress_ovwarn=false) ?(cast=false) ik : (t -> t * overflow_info) = function None -> (None, {underflow=false; overflow=false}) | Some (x,y) -> - if Ints_t.compare x y > 0 then - (None,{underflow=false; overflow=false}) - else ( - let (min_ik, max_ik) = range ik in - let underflow = Ints_t.compare min_ik x > 0 in - let overflow = Ints_t.compare max_ik y < 0 in - let ov_info = { underflow = underflow && not suppress_ovwarn; overflow = overflow && not suppress_ovwarn } in - let v = - if underflow || overflow then - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (Ints_t.sub max_ik min_ik) in - let resdiff = Ints_t.abs (Ints_t.sub y x) in - if Ints_t.compare resdiff diff > 0 then - top_of ik - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if Ints_t.compare l u <= 0 then - Some (l, u) - else - (* Interval that wraps around (begins to the right of its end). We can not represent such intervals *) - top_of ik - else if not cast && should_ignore_overflow ik then - let tl, tu = BatOption.get @@ top_of ik in - Some (Ints_t.max tl x, Ints_t.min tu y) - else - top_of ik - else - Some (x,y) - in - (v, ov_info) - ) - - let leq (x:t) (y:t) = - match x, y with - | None, _ -> true - | Some _, None -> false - | Some (x1,x2), Some (y1,y2) -> Ints_t.compare x1 y1 >= 0 && Ints_t.compare x2 y2 <= 0 - - let join ik (x:t) y = - match x, y with - | None, z | z, None -> z - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.min x1 y1, Ints_t.max x2 y2) |> fst - - let meet ik (x:t) y = - match x, y with - | None, z | z, None -> None - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.max x1 y1, Ints_t.min x2 y2) |> fst - - (* TODO: change to_int signature so it returns a big_int *) - let to_int x = Option.bind x (IArith.to_int) - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm ~suppress_ovwarn ik @@ Some (x,y) - let of_int ik (x: int_t) = of_interval ik (x,x) - let zero = Some IArith.zero - let one = Some IArith.one - let top_bool = Some IArith.top_bool - - let of_bool _ik = function true -> one | false -> zero - let to_bool (a: t) = match a with - | None -> None - | Some (l, u) when Ints_t.compare l Ints_t.zero = 0 && Ints_t.compare u Ints_t.zero = 0 -> Some false - | x -> if leq zero x then None else Some true - - let starting ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (fst (range ik), n) - - (* TODO: change signature of maximal, minimal to return big_int*) - let maximal = function None -> None | Some (x,y) -> Some y - let minimal = function None -> None | Some (x,y) -> Some x - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov t = norm ~cast:true t (* norm does all overflow handling *) - - let widen ik x y = - match x, y with - | None, z | z, None -> z - | Some (l0,u0), Some (l1,u1) -> - let (min_ik, max_ik) = range ik in - let threshold = get_interval_threshold_widening () in - let l2 = - if Ints_t.compare l0 l1 = 0 then l0 - else if threshold then IArith.lower_threshold l1 min_ik - else min_ik - in - let u2 = - if Ints_t.compare u0 u1 = 0 then u0 - else if threshold then IArith.upper_threshold u1 max_ik - else max_ik - in - norm ik @@ Some (l2,u2) |> fst - let widen ik x y = - let r = widen ik x y in - if M.tracing && not (equal x y) then M.tracel "int" "interval widen %a %a -> %a" pretty x pretty y pretty r; - assert (leq x y); (* TODO: remove for performance reasons? *) - r - - let narrow ik x y = - match x, y with - | _,None | None, _ -> None - | Some (x1,x2), Some (y1,y2) -> - let threshold = get_interval_threshold_widening () in - let (min_ik, max_ik) = range ik in - let lr = if Ints_t.compare min_ik x1 = 0 || threshold && Ints_t.compare y1 x1 > 0 && IArith.is_lower_threshold x1 then y1 else x1 in - let ur = if Ints_t.compare max_ik x2 = 0 || threshold && Ints_t.compare y2 x2 < 0 && IArith.is_upper_threshold x2 then y2 else x2 in - norm ik @@ Some (lr,ur) |> fst - - - let narrow ik x y = - if get_interval_narrow_by_meet () then - meet ik x y - else - narrow ik x y - - let log f ~annihilator ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_bool i1, to_bool i2 with - | Some x, _ when x = annihilator -> of_bool ik annihilator - | _, Some y when y = annihilator -> of_bool ik annihilator - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - let c_logor = log (||) ~annihilator:true - let c_logand = log (&&) ~annihilator:false - - let log1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_bool i1 with - | Some x -> of_bool ik (f ik x) - | _ -> top_of ik - - let c_lognot = log1 (fun _ik -> not) - - let bit f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - let bitcomp f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{underflow=false; overflow=false})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let logxor = bit (fun _ik -> Ints_t.logxor) - - let logand ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (Ints_t.logand x y) |> fst with Division_by_zero -> top_of ik) - | _, Some y when Ints_t.equal y Ints_t.zero -> of_int ik Ints_t.zero |> fst - | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst - | _ -> top_of ik - - let logor = bit (fun _ik -> Ints_t.logor) - - let bit1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_int i1 with - | Some x -> of_int ik (f ik x) |> fst - | _ -> top_of ik - - let lognot = bit1 (fun _ik -> Ints_t.lognot) - let shift_right = bitcomp (fun _ik x y -> Ints_t.shift_right x (Ints_t.to_int y)) - - let neg ?no_ov ik = function None -> (None,{underflow=false; overflow=false}) | Some x -> norm ik @@ Some (IArith.neg x) - - let binary_op_with_norm ?no_ov op ik x y = match x, y with - | None, None -> (None, {overflow=false; underflow= false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some x, Some y -> norm ik @@ Some (op x y) - - let add ?no_ov = binary_op_with_norm IArith.add - let mul ?no_ov = binary_op_with_norm IArith.mul - let sub ?no_ov = binary_op_with_norm IArith.sub - - let shift_left ik a b = - match is_bot a, is_bot b with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show a) (show b))) - | _ -> - match a, minimal b, maximal b with - | Some a, Some bl, Some bu when (Ints_t.compare bl Ints_t.zero >= 0) -> - (try - let r = IArith.shift_left a (Ints_t.to_int bl, Ints_t.to_int bu) in - norm ik @@ Some r - with Z.Overflow -> (top_of ik,{underflow=false; overflow=true})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let rem ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (xl, xu), Some (yl, yu) -> - if is_top_of ik x && is_top_of ik y then - (* This is needed to preserve soundness also on things bigger than int32 e.g. *) - (* x: 3803957176L -> T in Interval32 *) - (* y: 4209861404L -> T in Interval32 *) - (* x % y: 3803957176L -> T in Interval32 *) - (* T in Interval32 is [-2147483648,2147483647] *) - (* the code below computes [-2147483647,2147483647] for this though which is unsound *) - top_of ik - else - (* If we have definite values, Ints_t.rem will give a definite result. - * Otherwise we meet with a [range] the result can be in. - * This range is [0, min xu b] if x is positive, and [max xl -b, min xu b] if x can be negative. - * The precise bound b is one smaller than the maximum bound. Negative y give the same result as positive. *) - let pos x = if Ints_t.compare x Ints_t.zero < 0 then Ints_t.neg x else x in - let b = Ints_t.sub (Ints_t.max (pos yl) (pos yu)) Ints_t.one in - let range = if Ints_t.compare xl Ints_t.zero>= 0 then Some (Ints_t.zero, Ints_t.min xu b) else Some (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit (fun _ik -> Ints_t.rem) ik x y) range - - let rec div ?no_ov ik x y = - match x, y with - | None, None -> (bot (),{underflow=false; overflow=false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | (Some (x1,x2) as x), (Some (y1,y2) as y) -> - begin - let is_zero v = Ints_t.compare v Ints_t.zero = 0 in - match y1, y2 with - | l, u when is_zero l && is_zero u -> (top_of ik,{underflow=false; overflow=false}) (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> div ik (Some (x1,x2)) (Some (Ints_t.one,y2)) - | _, u when is_zero u -> div ik (Some (x1,x2)) (Some (y1, Ints_t.(neg one))) - | _ when leq (of_int ik (Ints_t.zero) |> fst) (Some (y1,y2)) -> (top_of ik,{underflow=false; overflow=false}) - | _ -> binary_op_with_norm IArith.div ik x y - end - - let ne ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik true - else if Ints_t.compare x2 y1 <= 0 && Ints_t.compare y2 x1 <= 0 then - of_bool ik false - else top_bool - - let eq ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 && Ints_t.compare x2 y1 <= 0 then - of_bool ik true - else if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik false - else top_bool - - let ge ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 then of_bool ik true - else if Ints_t.compare x2 y1 < 0 then of_bool ik false - else top_bool - - let le ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 <= 0 then of_bool ik true - else if Ints_t.compare y2 x1 < 0 then of_bool ik false - else top_bool - - let gt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 then of_bool ik true - else if Ints_t.compare x2 y1 <= 0 then of_bool ik false - else top_bool - - let lt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 < 0 then of_bool ik true - else if Ints_t.compare y2 x1 <= 0 then of_bool ik false - else top_bool - - let invariant_ikind e ik = function - | Some (x1, x2) -> - let (x1', x2') = BatTuple.Tuple2.mapn Ints_t.to_bigint (x1, x2) in - IntInvariant.of_interval e ik (x1', x2') - | None -> Invariant.none - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let shrink = function - | Some (l, u) -> (return None) <+> (GobQCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) - | None -> empty - in - QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) (fun x -> of_interval ik x |> fst ) pair_arb) - - let modulo n k = - let result = Ints_t.rem n k in - if Ints_t.compare result Ints_t.zero >= 0 then result - else Ints_t.add result k - - let refine_with_congruence ik (intv : t) (cong : (int_t * int_t ) option) : t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if Ints_t.equal m Ints_t.zero && (Ints_t.compare c x < 0 || Ints_t.compare c y > 0) then None - else if Ints_t.equal m Ints_t.zero then - Some (c, c) - else - let (min_ik, max_ik) = range ik in - let rcx = - if Ints_t.equal x min_ik then x else - Ints_t.add x (modulo (Ints_t.sub c x) (Ints_t.abs m)) in - let lcy = - if Ints_t.equal y max_ik then y else - Ints_t.sub y (modulo (Ints_t.sub y c) (Ints_t.abs m)) in - if Ints_t.compare rcx lcy > 0 then None - else if Ints_t.equal rcx lcy then norm ik @@ Some (rcx, rcx) |> fst - else norm ik @@ Some (rcx, lcy) |> fst - | _ -> None - - let refine_with_congruence ik x y = - let refn = refine_with_congruence ik x y in - if M.tracing then M.trace "refine" "int_refine_with_congruence %a %a -> %a" pretty x pretty y pretty refn; - refn - - let refine_with_interval ik a b = meet ik a b - - let refine_with_excl_list ik (intv : t) (excl : (int_t list * (int64 * int64)) option) : t = - match intv, excl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls, (rl, rh)) -> - let rec shrink op b = - let new_b = (op b (Ints_t.of_int(Bool.to_int(BatList.mem_cmp Ints_t.compare b ls)))) in - if not (Ints_t.equal b new_b) then shrink op new_b else new_b - in - let (min_ik, max_ik) = range ik in - let l' = if Ints_t.equal l min_ik then l else shrink Ints_t.add l in - let u' = if Ints_t.equal u max_ik then u else shrink Ints_t.sub u in - let intv' = norm ik @@ Some (l', u') |> fst in - let range = norm ~suppress_ovwarn:true ik (Some (Ints_t.of_bigint (Size.min_from_bit_range rl), Ints_t.of_bigint (Size.max_from_bit_range rh))) |> fst in - meet ik intv' range - - let refine_with_incl_list ik (intv: t) (incl : (int_t list) option) : t = - match intv, incl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls) -> - let rec min m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> min (Some x) xs | Some m -> if Ints_t.compare m x < 0 then min (Some m) xs else min (Some x) xs in - let rec max m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> max (Some x) xs | Some m -> if Ints_t.compare m x > 0 then max (Some m) xs else max (Some x) xs in - match min None ls, max None ls with - | Some m1, Some m2 -> refine_with_interval ik (Some(l, u)) (Some (m1, m2)) - | _, _-> intv - - let project ik p t = t -end - -(** IntervalSetFunctor that is not just disjunctive completion of intervals, but attempts to be precise for wraparound arithmetic for unsigned types *) -module IntervalSetFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) list = -struct - - module Interval = IntervalFunctor (Ints_t) - module IArith = IntervalArith (Ints_t) - - - let name () = "interval_sets" - - type int_t = Ints_t.t - - let (>.) a b = Ints_t.compare a b > 0 - let (=.) a b = Ints_t.compare a b = 0 - let (<.) a b = Ints_t.compare a b < 0 - let (>=.) a b = Ints_t.compare a b >= 0 - let (<=.) a b = Ints_t.compare a b <= 0 - let (+.) a b = Ints_t.add a b - let (-.) a b = Ints_t.sub a b - - (* - Each domain's element is guaranteed to be in canonical form. That is, each interval contained - inside the set does not overlap with each other and they are not adjacent. - *) - type t = (Ints_t.t * Ints_t.t) list [@@deriving eq, hash, ord] - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - - let top_of ik = [range ik] - - let bot () = [] - - let bot_of ik = bot () - - let show (x: t) = - let show_interval i = Printf.sprintf "[%s, %s]" (Ints_t.to_string (fst i)) (Ints_t.to_string (snd i)) in - List.fold_left (fun acc i -> (show_interval i) :: acc) [] x |> List.rev |> String.concat ", " |> Printf.sprintf "[%s]" - - (* New type definition for the sweeping line algorithm used for implementing join/meet functions. *) - type event = Enter of Ints_t.t | Exit of Ints_t.t - - let unbox_event = function Enter x -> x | Exit x -> x - - let cmp_events x y = - (* Deliberately comparing ints first => Cannot be derived *) - let res = Ints_t.compare (unbox_event x) (unbox_event y) in - if res <> 0 then res - else - begin - match (x, y) with - | (Enter _, Exit _) -> -1 - | (Exit _, Enter _) -> 1 - | (_, _) -> 0 - end - - let interval_set_to_events (xs: t) = - List.concat_map (fun (a, b) -> [Enter a; Exit b]) xs - - let two_interval_sets_to_events (xs: t) (ys: t) = - let xs = interval_set_to_events xs in - let ys = interval_set_to_events ys in - List.merge cmp_events xs ys - - (* Using the sweeping line algorithm, combined_event_list returns a new event list representing the intervals in which at least n intervals in xs overlap - This function is used for both join and meet operations with different parameter n: 1 for join, 2 for meet *) - let combined_event_list lattice_op (xs:event list) = - let l = match lattice_op with `Join -> 1 | `Meet -> 2 in - let aux (interval_count, acc) = function - | Enter x -> (interval_count + 1, if (interval_count + 1) >= l && interval_count < l then (Enter x)::acc else acc) - | Exit x -> (interval_count - 1, if interval_count >= l && (interval_count - 1) < l then (Exit x)::acc else acc) - in - List.fold_left aux (0, []) xs |> snd |> List.rev - - let rec events_to_intervals = function - | [] -> [] - | (Enter x)::(Exit y)::xs -> (x, y)::(events_to_intervals xs) - | _ -> failwith "Invalid events list" - - let remove_empty_gaps (xs: t) = - let aux acc (l, r) = match acc with - | ((a, b)::acc') when (b +. Ints_t.one) >=. l -> (a, r)::acc' - | _ -> (l, r)::acc - in - List.fold_left aux [] xs |> List.rev - - let canonize (xs: t) = - interval_set_to_events xs |> - List.sort cmp_events |> - combined_event_list `Join |> - events_to_intervals |> - remove_empty_gaps - - let unop (x: t) op = match x with - | [] -> [] - | _ -> canonize @@ List.concat_map op x - - let binop (x: t) (y: t) op : t = match x, y with - | [], _ -> [] - | _, [] -> [] - | _, _ -> canonize @@ List.concat_map op (BatList.cartesian_product x y) - - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let minimal = function - | [] -> None - | (x, _)::_ -> Some x - - let maximal = function - | [] -> None - | xs -> Some (BatList.last xs |> snd) - - let equal_to_interval i (a, b) = - if a =. b && b =. i then - `Eq - else if a <=. i && i <=. b then - `Top - else - `Neq - - let equal_to i xs = match List.map (equal_to_interval i) xs with - | [] -> failwith "unsupported: equal_to with bottom" - | [`Eq] -> `Eq - | ys when List.for_all ((=) `Neq) ys -> `Neq - | _ -> `Top - - let norm_interval ?(suppress_ovwarn=false) ?(cast=false) ik (x,y) : t*overflow_info = - if x >. y then - ([],{underflow=false; overflow=false}) - else - let (min_ik, max_ik) = range ik in - let underflow = min_ik >. x in - let overflow = max_ik <. y in - let v = if underflow || overflow then - begin - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (max_ik -. min_ik) in - let resdiff = Ints_t.abs (y -. x) in - if resdiff >. diff then - [range ik] - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if l <=. u then - [(l, u)] - else - (* Interval that wraps around (begins to the right of its end). We CAN represent such intervals *) - [(min_ik, u); (l, max_ik)] - else if not cast && should_ignore_overflow ik then - [Ints_t.max min_ik x, Ints_t.min max_ik y] - else - [range ik] - end - else - [(x,y)] - in - if suppress_ovwarn then (v, {underflow=false; overflow=false}) else (v, {underflow; overflow}) - - let norm_intvs ?(suppress_ovwarn=false) ?(cast=false) (ik:ikind) (xs: t) : t*overflow_info = - let res = List.map (norm_interval ~suppress_ovwarn ~cast ik) xs in - let intvs = List.concat_map fst res in - let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in - let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in - (canonize intvs,{underflow; overflow}) - - let binary_op_with_norm op (ik:ikind) (x: t) (y: t) : t*overflow_info = match x, y with - | [], _ -> ([],{overflow=false; underflow=false}) - | _, [] -> ([],{overflow=false; underflow=false}) - | _, _ -> norm_intvs ik @@ List.concat_map (fun (x,y) -> [op x y]) (BatList.cartesian_product x y) - - let binary_op_with_ovc (x: t) (y: t) op : t*overflow_info = match x, y with - | [], _ -> ([],{overflow=false; underflow=false}) - | _, [] -> ([],{overflow=false; underflow=false}) - | _, _ -> - let res = List.map op (BatList.cartesian_product x y) in - let intvs = List.concat_map fst res in - let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in - let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in - (canonize intvs,{underflow; overflow}) - - let unary_op_with_norm op (ik:ikind) (x: t) = match x with - | [] -> ([],{overflow=false; underflow=false}) - | _ -> norm_intvs ik @@ List.concat_map (fun x -> [op x]) x - - let rec leq (xs: t) (ys: t) = - let leq_interval (al, au) (bl, bu) = al >=. bl && au <=. bu in - match xs, ys with - | [], _ -> true - | _, [] -> false - | (xl,xr)::xs', (yl,yr)::ys' -> - if leq_interval (xl,xr) (yl,yr) then - leq xs' ys - else if xr <. yl then - false - else - leq xs ys' - - let join ik (x: t) (y: t): t = - two_interval_sets_to_events x y |> - combined_event_list `Join |> - events_to_intervals |> - remove_empty_gaps - - let meet ik (x: t) (y: t): t = - two_interval_sets_to_events x y |> - combined_event_list `Meet |> - events_to_intervals - - let to_int = function - | [x] -> IArith.to_int x - | _ -> None - - let zero = [IArith.zero] - let one = [IArith.one] - let top_bool = [IArith.top_bool] - - let not_bool (x:t) = - let is_false x = equal x zero in - let is_true x = equal x one in - if is_true x then zero else if is_false x then one else top_bool - - let to_bool = function - | [(l,u)] when l =. Ints_t.zero && u =. Ints_t.zero -> Some false - | x -> if leq zero x then None else Some true - - let of_bool _ = function true -> one | false -> zero - - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm_interval ~suppress_ovwarn ~cast:false ik (x,y) - - let of_int ik (x: int_t) = of_interval ik (x, x) - - let lt ik x y = - match x, y with - | [], [] -> bot_of ik - | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> - let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in - let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in - if max_x <. min_y then - of_bool ik true - else if min_x >=. max_y then - of_bool ik false - else - top_bool - - let le ik x y = - match x, y with - | [], [] -> bot_of ik - | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> - let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in - let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in - if max_x <=. min_y then - of_bool ik true - else if min_x >. max_y then - of_bool ik false - else - top_bool - - let gt ik x y = not_bool @@ le ik x y - - let ge ik x y = not_bool @@ lt ik x y - - let eq ik x y = match x, y with - | (a, b)::[], (c, d)::[] when a =. b && c =. d && a =. c -> - one - | _ -> - if is_bot (meet ik x y) then - zero - else - top_bool - - let ne ik x y = not_bool @@ eq ik x y - let interval_to_int i = Interval.to_int (Some i) - let interval_to_bool i = Interval.to_bool (Some i) - - let log f ik (i1, i2) = - match (interval_to_bool i1, interval_to_bool i2) with - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - - let bit f ik (i1, i2) = - match (interval_to_int i1), (interval_to_int i2) with - | Some x, Some y -> (try of_int ik (f x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - - let bitcomp f ik (i1, i2) = - match (interval_to_int i1, interval_to_int i2) with - | Some x, Some y -> (try of_int ik (f x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{overflow=false; underflow=false})) - | _, _ -> (top_of ik,{overflow=false; underflow=false}) - - let logand ik x y = - let interval_logand = bit Ints_t.logand ik in - binop x y interval_logand - - let logor ik x y = - let interval_logor = bit Ints_t.logor ik in - binop x y interval_logor - - let logxor ik x y = - let interval_logxor = bit Ints_t.logxor ik in - binop x y interval_logxor - - let lognot ik x = - let interval_lognot i = - match interval_to_int i with - | Some x -> of_int ik (Ints_t.lognot x) |> fst - | _ -> top_of ik - in - unop x interval_lognot - - let shift_left ik x y = - let interval_shiftleft = bitcomp (fun x y -> Ints_t.shift_left x (Ints_t.to_int y)) ik in - binary_op_with_ovc x y interval_shiftleft - - let shift_right ik x y = - let interval_shiftright = bitcomp (fun x y -> Ints_t.shift_right x (Ints_t.to_int y)) ik in - binary_op_with_ovc x y interval_shiftright - - let c_lognot ik x = - let log1 f ik i1 = - match interval_to_bool i1 with - | Some x -> of_bool ik (f x) - | _ -> top_of ik - in - let interval_lognot = log1 not ik in - unop x interval_lognot - - let c_logand ik x y = - let interval_logand = log (&&) ik in - binop x y interval_logand - - let c_logor ik x y = - let interval_logor = log (||) ik in - binop x y interval_logor - - let add ?no_ov = binary_op_with_norm IArith.add - let sub ?no_ov = binary_op_with_norm IArith.sub - let mul ?no_ov = binary_op_with_norm IArith.mul - let neg ?no_ov = unary_op_with_norm IArith.neg - - let div ?no_ov ik x y = - let rec interval_div x (y1, y2) = begin - let top_of ik = top_of ik |> List.hd in - let is_zero v = v =. Ints_t.zero in - match y1, y2 with - | l, u when is_zero l && is_zero u -> top_of ik (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> interval_div x (Ints_t.one,y2) - | _, u when is_zero u -> interval_div x (y1, Ints_t.(neg one)) - | _ when leq (of_int ik (Ints_t.zero) |> fst) ([(y1,y2)]) -> top_of ik - | _ -> IArith.div x (y1, y2) - end - in binary_op_with_norm interval_div ik x y - - let rem ik x y = - let interval_rem (x, y) = - if Interval.is_top_of ik (Some x) && Interval.is_top_of ik (Some y) then - top_of ik - else - let (xl, xu) = x in let (yl, yu) = y in - let pos x = if x <. Ints_t.zero then Ints_t.neg x else x in - let b = (Ints_t.max (pos yl) (pos yu)) -. Ints_t.one in - let range = if xl >=. Ints_t.zero then (Ints_t.zero, Ints_t.min xu b) else (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit Ints_t.rem ik (x, y)) [range] - in - binop x y interval_rem - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik x = norm_intvs ~cast:true ik x - - (* - narrows down the extremeties of xs if they are equal to boundary values of the ikind with (possibly) narrower values from ys - *) - let narrow ik xs ys = match xs ,ys with - | [], _ -> [] | _ ,[] -> xs - | _, _ -> - let min_xs = minimal xs |> Option.get in - let max_xs = maximal xs |> Option.get in - let min_ys = minimal ys |> Option.get in - let max_ys = maximal ys |> Option.get in - let min_range,max_range = range ik in - let threshold = get_interval_threshold_widening () in - let min = if min_xs =. min_range || threshold && min_ys >. min_xs && IArith.is_lower_threshold min_xs then min_ys else min_xs in - let max = if max_xs =. max_range || threshold && max_ys <. max_xs && IArith.is_upper_threshold max_xs then max_ys else max_xs in - xs - |> (function (_, y)::z -> (min, y)::z | _ -> []) - |> List.rev - |> (function (x, _)::z -> (x, max)::z | _ -> []) - |> List.rev - - (* - 1. partitions the intervals of xs by assigning each of them to the an interval in ys that includes it. - and joins all intervals in xs assigned to the same interval in ys as one interval. - 2. checks for every pair of adjacent pairs whether the pairs did approach (if you compare the intervals from xs and ys) and merges them if it is the case. - 3. checks whether partitions at the extremeties are approaching infinity (and expands them to infinity. in that case) - - The expansion (between a pair of adjacent partitions or at extremeties ) stops at a threshold. - *) - let widen ik xs ys = - let (min_ik,max_ik) = range ik in - let threshold = get_bool "ana.int.interval_threshold_widening" in - let upper_threshold (_,u) = IArith.upper_threshold u max_ik in - let lower_threshold (l,_) = IArith.lower_threshold l min_ik in - (*obtain partitioning of xs intervals according to the ys interval that includes them*) - let rec interval_sets_to_partitions (ik: ikind) (acc : (int_t * int_t) option) (xs: t) (ys: t)= - match xs,ys with - | _, [] -> [] - | [], (y::ys) -> (acc,y):: interval_sets_to_partitions ik None [] ys - | (x::xs), (y::ys) when Interval.leq (Some x) (Some y) -> interval_sets_to_partitions ik (Interval.join ik acc (Some x)) xs (y::ys) - | (x::xs), (y::ys) -> (acc,y) :: interval_sets_to_partitions ik None (x::xs) ys - in - let interval_sets_to_partitions ik xs ys = interval_sets_to_partitions ik None xs ys in - (*merge a pair of adjacent partitions*) - let merge_pair ik (a,b) (c,d) = - let new_a = function - | None -> Some (upper_threshold b, upper_threshold b) - | Some (ax,ay) -> Some (ax, upper_threshold b) - in - let new_c = function - | None -> Some (lower_threshold d, lower_threshold d) - | Some (cx,cy) -> Some (lower_threshold d, cy) - in - if threshold && (lower_threshold d +. Ints_t.one) >. (upper_threshold b) then - [(new_a a,(fst b, upper_threshold b)); (new_c c, (lower_threshold d, snd d))] - else - [(Interval.join ik a c, (Interval.join ik (Some b) (Some d) |> Option.get))] - in - let partitions_are_approaching part_left part_right = match part_left, part_right with - | (Some (_, left_x), (_, left_y)), (Some (right_x, _), (right_y, _)) -> (right_x -. left_x) >. (right_y -. left_y) - | _,_ -> false - in - (*merge all approaching pairs of adjacent partitions*) - let rec merge_list ik = function - | [] -> [] - | x::y::xs when partitions_are_approaching x y -> merge_list ik ((merge_pair ik x y) @ xs) - | x::xs -> x :: merge_list ik xs - in - (*expands left extremity*) - let widen_left = function - | [] -> [] - | (None,(lb,rb))::ts -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (None, (lt,rb))::ts - | (Some (la,ra), (lb,rb))::ts when lb <. la -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (Some (la,ra),(lt,rb))::ts - | x -> x - in - (*expands right extremity*) - let widen_right x = - let map_rightmost = function - | [] -> [] - | (None,(lb,rb))::ts -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (None, (lb,ut))::ts - | (Some (la,ra), (lb,rb))::ts when ra <. rb -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (Some (la,ra),(lb,ut))::ts - | x -> x - in - List.rev x |> map_rightmost |> List.rev - in - interval_sets_to_partitions ik xs ys |> merge_list ik |> widen_left |> widen_right |> List.map snd - - let starting ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (fst (range ik), n) - - let invariant_ikind e ik xs = - List.map (fun x -> Interval.invariant_ikind e ik (Some x)) xs |> - let open Invariant in List.fold_left (||) (bot ()) - - let modulo n k = - let result = Ints_t.rem n k in - if result >=. Ints_t.zero then result - else result +. k - - let refine_with_congruence ik (intvs: t) (cong: (int_t * int_t ) option): t = - let refine_with_congruence_interval ik (cong : (int_t * int_t ) option) (intv : (int_t * int_t ) option): t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if m =. Ints_t.zero && (c <. x || c >. y) then [] - else if m =. Ints_t.zero then - [(c, c)] - else - let (min_ik, max_ik) = range ik in - let rcx = - if x =. min_ik then x else - x +. (modulo (c -. x) (Ints_t.abs m)) in - let lcy = - if y =. max_ik then y else - y -. (modulo (y -. c) (Ints_t.abs m)) in - if rcx >. lcy then [] - else if rcx =. lcy then norm_interval ik (rcx, rcx) |> fst - else norm_interval ik (rcx, lcy) |> fst - | _ -> [] - in - List.concat_map (fun x -> refine_with_congruence_interval ik cong (Some x)) intvs - - let refine_with_interval ik xs = function None -> [] | Some (a,b) -> meet ik xs [(a,b)] - - let refine_with_incl_list ik intvs = function - | None -> intvs - | Some xs -> meet ik intvs (List.map (fun x -> (x,x)) xs) - - let excl_range_to_intervalset (ik: ikind) ((min, max): int_t * int_t) (excl: int_t): t = - let intv1 = (min, excl -. Ints_t.one) in - let intv2 = (excl +. Ints_t.one, max) in - norm_intvs ik ~suppress_ovwarn:true [intv1 ; intv2] |> fst - - let of_excl_list ik (excls: int_t list) = - let excl_list = List.map (excl_range_to_intervalset ik (range ik)) excls in - let res = List.fold_left (meet ik) (top_of ik) excl_list in - res - - let refine_with_excl_list ik (intv : t) = function - | None -> intv - | Some (xs, range) -> - let excl_to_intervalset (ik: ikind) ((rl, rh): (int64 * int64)) (excl: int_t): t = - excl_range_to_intervalset ik (Ints_t.of_bigint (Size.min_from_bit_range rl),Ints_t.of_bigint (Size.max_from_bit_range rh)) excl - in - let excl_list = List.map (excl_to_intervalset ik range) xs in - List.fold_left (meet ik) intv excl_list - - let project ik p t = t - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let list_pair_arb = QCheck.small_list pair_arb in - let canonize_randomly_generated_list = (fun x -> norm_intvs ik x |> fst) in - let shrink xs = GobQCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list - in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) canonize_randomly_generated_list list_pair_arb) -end - -module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t = struct - include D - - let add ?no_ov ik x y = fst @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = fst @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = fst @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = fst @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = fst @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = fst @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = fst @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = fst @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = fst @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = fst @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = fst @@ D.shift_left ik x y - - let shift_right ik x y = fst @@ D.shift_right ik x y -end - -module IntIkind = struct let ikind () = Cil.IInt end -module Interval = IntervalFunctor (IntOps.BigIntOps) -module Interval32 = IntDomWithDefaultIkind (IntDomLifter (SOverflowUnlifter (IntervalFunctor (IntOps.Int64Ops)))) (IntIkind) -module IntervalSet = IntervalSetFunctor (IntOps.BigIntOps) -module Integers (Ints_t : IntOps.IntOps): IkindUnawareS with type t = Ints_t.t and type int_t = Ints_t.t = (* no top/bot, order is <= *) -struct - include Printable.Std - let name () = "integers" - type t = Ints_t.t [@@deriving eq, ord, hash] - type int_t = Ints_t.t - let top () = raise Unknown - let bot () = raise Error - let top_of ik = top () - let bot_of ik = bot () - let show (x: Ints_t.t) = Ints_t.to_string x - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - (* is_top and is_bot are never called, but if they were, the Std impl would raise their exception, so we overwrite them: *) - let is_top _ = false - let is_bot _ = false - - let equal_to i x = if i > x then `Neq else `Top - let leq x y = x <= y - let join x y = if Ints_t.compare x y > 0 then x else y - let widen = join - let meet x y = if Ints_t.compare x y > 0 then y else x - let narrow = meet - - let of_bool x = if x then Ints_t.one else Ints_t.zero - let to_bool' x = x <> Ints_t.zero - let to_bool x = Some (to_bool' x) - let of_int x = x - let to_int x = Some x - - let neg = Ints_t.neg - let add = Ints_t.add (* TODO: signed overflow is undefined behavior! *) - let sub = Ints_t.sub - let mul = Ints_t.mul - let div = Ints_t.div - let rem = Ints_t.rem - let lt n1 n2 = of_bool (n1 < n2) - let gt n1 n2 = of_bool (n1 > n2) - let le n1 n2 = of_bool (n1 <= n2) - let ge n1 n2 = of_bool (n1 >= n2) - let eq n1 n2 = of_bool (n1 = n2) - let ne n1 n2 = of_bool (n1 <> n2) - let lognot = Ints_t.lognot - let logand = Ints_t.logand - let logor = Ints_t.logor - let logxor = Ints_t.logxor - let shift_left n1 n2 = Ints_t.shift_left n1 (Ints_t.to_int n2) - let shift_right n1 n2 = Ints_t.shift_right n1 (Ints_t.to_int n2) - let c_lognot n1 = of_bool (not (to_bool' n1)) - let c_logand n1 n2 = of_bool ((to_bool' n1) && (to_bool' n2)) - let c_logor n1 n2 = of_bool ((to_bool' n1) || (to_bool' n2)) - let cast_to ?(suppress_ovwarn=false) ?torg t x = failwith @@ "Cast_to not implemented for " ^ (name ()) ^ "." - let arbitrary ik = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 (* TODO: use ikind *) - let invariant _ _ = Invariant.none (* TODO *) -end - -module FlatPureIntegers: IkindUnawareS with type t = int64 and type int_t = int64 = (* Integers, but raises Unknown/Error on join/meet *) -struct - include Integers(IntOps.Int64Ops) - let top () = raise Unknown - let bot () = raise Error - let leq = equal - let pretty_diff () (x,y) = Pretty.dprintf "Integer %a instead of %a" pretty x pretty y - let join x y = if equal x y then x else top () - let meet x y = if equal x y then x else bot () -end - -module Flat (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Lift, but goes to `Top/`Bot if Base raises Unknown/Error *) -struct - type int_t = Base.int_t - include Lattice.FlatConf (struct - include Printable.DefaultConf - let top_name = "Unknown int" - let bot_name = "Error int" - end) (Base) - - let top_of ik = top () - let bot_of ik = bot () - - - let name () = "flat integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ikind x = top_of ikind - let ending ?(suppress_ovwarn=false) ikind x = top_of ikind - let maximal x = None - let minimal x = None - - let lift1 f x = match x with - | `Lifted x -> - (try `Lifted (f x) with Unknown -> `Top | Error -> `Bot) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> - (try `Lifted (f x y) with Unknown -> `Top | Error -> `Bot) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Lift (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Flat, but does not go to `Top/Bot` if Base raises Unknown/Error *) -struct - include Lattice.LiftPO (struct - include Printable.DefaultConf - let top_name = "MaxInt" - let bot_name = "MinInt" - end) (Base) - type int_t = Base.int_t - let top_of ik = top () - let bot_of ik = bot () - include StdTop (struct type nonrec t = t let top_of = top_of end) - - let name () = "lifted integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let lift1 f x = match x with - | `Lifted x -> `Lifted (f x) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> `Lifted (f x y) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Flattened = Flat (Integers (IntOps.Int64Ops)) -module Lifted = Lift (Integers (IntOps.Int64Ops)) - -module Reverse (Base: IkindUnawareS) = -struct - include Base - include (Lattice.Reverse (Base) : Lattice.S with type t := Base.t) -end - -module BISet = struct - include SetDomain.Make (IntOps.BigIntOps) - let is_singleton s = cardinal s = 1 -end - -(* The module [Exclusion] constains common functionality about handling of exclusion sets between [DefExc] and [Enums] *) -module Exclusion = -struct - module R = Interval32 - (* We use these types for the functions in this module to make the intended meaning more explicit *) - type t = Exc of BISet.t * Interval32.t - type inc = Inc of BISet.t [@@unboxed] - let max_of_range r = Size.max_from_bit_range (Option.get (R.maximal r)) - let min_of_range r = Size.min_from_bit_range (Option.get (R.minimal r)) - let cardinality_of_range r = Z.succ (Z.add (Z.neg (min_of_range r)) (max_of_range r)) - - let cardinality_BISet s = - Z.of_int (BISet.cardinal s) - - let leq_excl_incl (Exc (xs, r)) (Inc ys) = - (* For a <= b to hold, the cardinalities must fit, i.e. |a| <= |b|, which implies |min_r, max_r| - |xs| <= |ys|. We check this first. *) - let lower_bound_cardinality_a = Z.sub (cardinality_of_range r) (cardinality_BISet xs) in - let card_b = cardinality_BISet ys in - if Z.compare lower_bound_cardinality_a card_b > 0 then - false - else (* The cardinality did fit, so we check for all elements that are represented by range r, whether they are in (xs union ys) *) - let min_a = min_of_range r in - let max_a = max_of_range r in - GobZ.for_all_range (fun el -> BISet.mem el xs || BISet.mem el ys) (min_a, max_a) - - let leq (Exc (xs, r)) (Exc (ys, s)) = - let min_a, max_a = min_of_range r, max_of_range r in - let excluded_check = BISet.for_all (fun y -> BISet.mem y xs || Z.compare y min_a < 0 || Z.compare y max_a > 0) ys in (* if true, then the values ys, that are not in b, also do not occur in a *) - if not excluded_check - then false - else begin (* Check whether all elements that are in the range r, but not in s, are in xs, i.e. excluded. *) - if R.leq r s then true - else begin if Z.compare (cardinality_BISet xs) (Z.sub (cardinality_of_range r) (cardinality_of_range s)) >= 0 (* Check whether the number of excluded elements in a is as least as big as |min_r, max_r| - |min_s, max_s| *) - then - let min_b, max_b = min_of_range s, max_of_range s in - let leq1 = (* check whether the elements in [r_l; s_l-1] are all in xs, i.e. excluded *) - if Z.compare min_a min_b < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (min_a, Z.pred min_b) - else - true - in - let leq2 () = (* check whether the elements in [s_u+1; r_u] are all in xs, i.e. excluded *) - if Z.compare max_b max_a < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (Z.succ max_b, max_a) - else - true - in - leq1 && (leq2 ()) - else - false - end - end -end module DefExc : S with type int_t = Z.t = (* definite or set of excluded values *) struct @@ -2375,35 +479,3 @@ struct let project ik p t = t end - -module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct - - include D - - let lift v = (v, {overflow=false; underflow=false}) - - let add ?no_ov ik x y = lift @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = lift @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = lift @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = lift @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = lift @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = lift @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = lift @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = lift @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = lift @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = lift @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = lift @@ D.shift_left ik x y - - let shift_right ik x y = lift @@ D.shift_right ik x y - -end From a35c289d246d2fc55ea993f53ded147db91c0ae1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:03:24 +0300 Subject: [PATCH 469/566] Remove DefExc from IntDomain0 --- src/cdomain/value/cdomains/intDomain0.ml | 479 ----------------------- 1 file changed, 479 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/intDomain0.ml index 1cda533c55..0bcfa6ae44 100644 --- a/src/cdomain/value/cdomains/intDomain0.ml +++ b/src/cdomain/value/cdomains/intDomain0.ml @@ -1897,485 +1897,6 @@ struct end end -module DefExc : S with type int_t = Z.t = (* definite or set of excluded values *) -struct - module S = BISet - module R = Interval32 (* range for exclusion *) - - (* Ikind used for intervals representing the domain *) - let range_ikind = Cil.IInt - let size t = R.of_interval range_ikind (let a,b = Size.bits_i64 t in Int64.neg a,b) - - - type t = [ - | `Excluded of S.t * R.t - | `Definite of Z.t - | `Bot - ] [@@deriving eq, ord, hash] - type int_t = Z.t - let name () = "def_exc" - - - let top_range = R.of_interval range_ikind (-99L, 99L) (* Since there is no top ikind we use a range that includes both ILongLong [-63,63] and IULongLong [0,64]. Only needed for intermediate range computation on longs. Correct range is set by cast. *) - let top () = `Excluded (S.empty (), top_range) - let bot () = `Bot - let top_of ik = `Excluded (S.empty (), size ik) - let bot_of ik = bot () - - let show x = - let short_size x = "("^R.show x^")" in - match x with - | `Bot -> "Error int" - | `Definite x -> Z.to_string x - (* Print the empty exclusion as if it was a distinct top element: *) - | `Excluded (s,l) when S.is_empty s -> "Unknown int" ^ short_size l - (* Prepend the exclusion sets with something: *) - | `Excluded (s,l) -> "Not " ^ S.show s ^ short_size l - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let maximal = function - | `Definite x -> Some x - | `Excluded (s,r) -> Some (Exclusion.max_of_range r) - | `Bot -> None - - let minimal = function - | `Definite x -> Some x - | `Excluded (s,r) -> Some (Exclusion.min_of_range r) - | `Bot -> None - - let in_range r i = - if Z.compare i Z.zero < 0 then - let lowerb = Exclusion.min_of_range r in - Z.compare lowerb i <= 0 - else - let upperb = Exclusion.max_of_range r in - Z.compare i upperb <= 0 - - let is_top x = x = top () - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Definite x -> if i = x then `Eq else `Neq - | `Excluded (s,r) -> if S.mem i s then `Neq else `Top - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik = function - | `Excluded (s,r) -> - let r' = size ik in - if R.leq r r' then (* upcast -> no change *) - `Excluded (s, r) - else if ik = IBool then (* downcast to bool *) - if S.mem Z.zero s then - `Definite Z.one - else - `Excluded (S.empty(), r') - else - (* downcast: may overflow *) - (* let s' = S.map (Size.cast ik) s in *) - (* We want to filter out all i in s' where (t)x with x in r could be i. *) - (* Since this is hard to compute, we just keep all i in s' which overflowed, since those are safe - all i which did not overflow may now be possible due to overflow of r. *) - (* S.diff s' s, r' *) - (* The above is needed for test 21/03, but not sound! See example https://github.com/goblint/analyzer/pull/95#discussion_r483023140 *) - `Excluded (S.empty (), r') - | `Definite x -> `Definite (Size.cast ik x) - | `Bot -> `Bot - - (* Wraps definite values and excluded values according to the ikind. - * For an `Excluded s,r , assumes that r is already an overapproximation of the range of possible values. - * r might be larger than the possible range of this type; the range of the returned `Excluded set will be within the bounds of the ikind. - *) - let norm ik v = - match v with - | `Excluded (s, r) -> - let possibly_overflowed = not (R.leq r (size ik)) || not (S.for_all (in_range (size ik)) s) in - (* If no overflow occurred, just return x *) - if not possibly_overflowed then ( - v - ) - (* Else, if an overflow might have occurred but we should just ignore it *) - else if should_ignore_overflow ik then ( - let r = size ik in - (* filter out excluded elements that are not in the range *) - let mapped_excl = S.filter (in_range r) s in - `Excluded (mapped_excl, r) - ) - (* Else, if an overflow occurred that we should not treat with wrap-around, go to top *) - else if not (should_wrap ik) then ( - top_of ik - ) else ( - (* Else an overflow occurred that we should treat with wrap-around *) - let r = size ik in - (* Perform a wrap-around for unsigned values and for signed values (if configured). *) - let mapped_excl = S.map (fun excl -> Size.cast ik excl) s in - match ik with - | IBool -> - begin match S.mem Z.zero mapped_excl, S.mem Z.one mapped_excl with - | false, false -> `Excluded (mapped_excl, r) (* Not {} -> Not {} *) - | true, false -> `Definite Z.one (* Not {0} -> 1 *) - | false, true -> `Definite Z.zero (* Not {1} -> 0 *) - | true, true -> `Bot (* Not {0, 1} -> bot *) - end - | ik -> - `Excluded (mapped_excl, r) - ) - | `Definite x -> - let min, max = Size.range ik in - (* Perform a wrap-around for unsigned values and for signed values (if configured). *) - if should_wrap ik then ( - cast_to ik v - ) - else if Z.compare min x <= 0 && Z.compare x max <= 0 then ( - v - ) - else if should_ignore_overflow ik then ( - M.warn ~category:M.Category.Integer.overflow "DefExc: Value was outside of range, indicating overflow, but 'sem.int.signed_overflow' is 'assume_none' -> Returned Bot"; - `Bot - ) - else ( - top_of ik - ) - | `Bot -> `Bot - - let leq x y = match (x,y) with - (* `Bot <= x is always true *) - | `Bot, _ -> true - (* Anything except bot <= bot is always false *) - | _, `Bot -> false - (* Two known values are leq whenever equal *) - | `Definite (x: int_t), `Definite y -> x = y - (* A definite value is leq all exclusion sets that don't contain it *) - | `Definite x, `Excluded (s,r) -> in_range r x && not (S.mem x s) - (* No finite exclusion set can be leq than a definite value *) - | `Excluded (xs, xr), `Definite d -> - Exclusion.(leq_excl_incl (Exc (xs, xr)) (Inc (S.singleton d))) - | `Excluded (xs,xr), `Excluded (ys,yr) -> - Exclusion.(leq (Exc (xs,xr)) (Exc (ys, yr))) - - let join' ?range ik x y = - match (x,y) with - (* The least upper bound with the bottom element: *) - | `Bot, x -> x - | x, `Bot -> x - (* The case for two known values: *) - | `Definite (x: int_t), `Definite y -> - (* If they're equal, it's just THAT value *) - if x = y then `Definite x - (* Unless one of them is zero, we can exclude it: *) - else - let a,b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval range_ikind a) (R.of_interval range_ikind b) in - `Excluded ((if Z.equal x Z.zero || Z.equal y Z.zero then S.empty () else S.singleton Z.zero), r) - (* A known value and an exclusion set... the definite value should no - * longer be excluded: *) - | `Excluded (s,r), `Definite x - | `Definite x, `Excluded (s,r) -> - if not (in_range r x) then - let a = R.of_interval range_ikind (Size.min_range_sign_agnostic x) in - `Excluded (S.remove x s, R.join a r) - else - `Excluded (S.remove x s, r) - (* For two exclusion sets, only their intersection can be excluded: *) - | `Excluded (x,wx), `Excluded (y,wy) -> `Excluded (S.inter x y, range |? R.join wx wy) - - let join ik = join' ik - - - let widen ik x y = - if get_def_exc_widen_by_join () then - join' ik x y - else if equal x y then - x - else - join' ~range:(size ik) ik x y - - - let meet ik x y = - match (x,y) with - (* Greatest LOWER bound with the least element is trivial: *) - | `Bot, _ -> `Bot - | _, `Bot -> `Bot - (* Definite elements are either equal or the glb is bottom *) - | `Definite x, `Definite y -> if x = y then `Definite x else `Bot - (* The glb of a definite element and an exclusion set is either bottom or - * just the element itself, if it isn't in the exclusion set *) - | `Excluded (s,r), `Definite x - | `Definite x, `Excluded (s,r) -> if S.mem x s || not (in_range r x) then `Bot else `Definite x - (* The greatest lower bound of two exclusion sets is their union, this is - * just DeMorgans Law *) - | `Excluded (x,r1), `Excluded (y,r2) -> - let r' = R.meet r1 r2 in - let s' = S.union x y |> S.filter (in_range r') in - `Excluded (s', r') - - let narrow ik x y = x - - let of_int ik x = norm ik @@ `Definite x - let to_int x = match x with - | `Definite x -> Some x - | _ -> None - - let from_excl ikind (s: S.t) = norm ikind @@ `Excluded (s, size ikind) - - let of_bool_cmp ik x = of_int ik (if x then Z.one else Z.zero) - let of_bool = of_bool_cmp - let to_bool x = - match x with - | `Definite x -> Some (IntOps.BigIntOps.to_bool x) - | `Excluded (s,r) when S.mem Z.zero s -> Some true - | _ -> None - let top_bool = `Excluded (S.empty (), R.of_interval range_ikind (0L, 1L)) - - let of_interval ?(suppress_ovwarn=false) ik (x,y) = - if Z.compare x y = 0 then - of_int ik x - else - let a, b = Size.min_range_sign_agnostic x, Size.min_range_sign_agnostic y in - let r = R.join (R.of_interval ~suppress_ovwarn range_ikind a) (R.of_interval ~suppress_ovwarn range_ikind b) in - let ex = if Z.gt x Z.zero || Z.lt y Z.zero then S.singleton Z.zero else S.empty () in - norm ik @@ (`Excluded (ex, r)) - - let starting ?(suppress_ovwarn=false) ikind x = - let _,u_ik = Size.range ikind in - of_interval ~suppress_ovwarn ikind (x, u_ik) - - let ending ?(suppress_ovwarn=false) ikind x = - let l_ik,_ = Size.range ikind in - of_interval ~suppress_ovwarn ikind (l_ik, x) - - let of_excl_list t l = - let r = size t in (* elements in l are excluded from the full range of t! *) - `Excluded (List.fold_right S.add l (S.empty ()), r) - let is_excl_list l = match l with `Excluded _ -> true | _ -> false - let to_excl_list (x:t) = match x with - | `Definite _ -> None - | `Excluded (s,r) -> Some (S.elements s, (Option.get (R.minimal r), Option.get (R.maximal r))) - | `Bot -> None - - let to_incl_list x = match x with - | `Definite x -> Some [x] - | `Excluded _ -> None - | `Bot -> None - - let apply_range f r = (* apply f to the min/max of the old range r to get a new range *) - (* If the Int64 might overflow on us during computation, we instead go to top_range *) - match R.minimal r, R.maximal r with - | _ -> - let rf m = (size % Size.min_for % f) (m r) in - let r1, r2 = rf Exclusion.min_of_range, rf Exclusion.max_of_range in - R.join r1 r2 - - (* Default behaviour for unary operators, simply maps the function to the - * DefExc data structure. *) - let lift1 f ik x = norm ik @@ match x with - | `Excluded (s,r) -> - let s' = S.map f s in - `Excluded (s', apply_range f r) - | `Definite x -> `Definite (f x) - | `Bot -> `Bot - - let lift2 f ik x y = norm ik (match x,y with - (* We don't bother with exclusion sets: *) - | `Excluded _, `Definite _ - | `Definite _, `Excluded _ - | `Excluded _, `Excluded _ -> top () - (* The good case: *) - | `Definite x, `Definite y -> - (try `Definite (f x y) with | Division_by_zero -> top ()) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) - - (* Default behaviour for binary operators that are injective in either - * argument, so that Exclusion Sets can be used: *) - let lift2_inj f ik x y = - let def_exc f x s r = `Excluded (S.map (f x) s, apply_range (f x) r) in - norm ik @@ - match x,y with - (* If both are exclusion sets, there isn't anything we can do: *) - | `Excluded _, `Excluded _ -> top () - (* A definite value should be applied to all members of the exclusion set *) - | `Definite x, `Excluded (s,r) -> def_exc f x s r - (* Same thing here, but we should flip the operator to map it properly *) - | `Excluded (s,r), `Definite x -> def_exc (Batteries.flip f) x s r - (* The good case: *) - | `Definite x, `Definite y -> `Definite (f x y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - (* The equality check: *) - let eq ik x y = match x,y with - (* Not much to do with two exclusion sets: *) - | `Excluded _, `Excluded _ -> top () - (* Is x equal to an exclusion set, if it is a member then NO otherwise we - * don't know: *) - | `Definite x, `Excluded (s,r) -> if S.mem x s then of_bool IInt false else top () - | `Excluded (s,r), `Definite x -> if S.mem x s then of_bool IInt false else top () - (* The good case: *) - | `Definite x, `Definite y -> of_bool IInt (x = y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - (* The inequality check: *) - let ne ik x y = match x,y with - (* Not much to do with two exclusion sets: *) - | `Excluded _, `Excluded _ -> top () - (* Is x unequal to an exclusion set, if it is a member then Yes otherwise we - * don't know: *) - | `Definite x, `Excluded (s,r) -> if S.mem x s then of_bool IInt true else top () - | `Excluded (s,r), `Definite x -> if S.mem x s then of_bool IInt true else top () - (* The good case: *) - | `Definite x, `Definite y -> of_bool IInt (x <> y) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - - let neg ?no_ov ik (x :t) = norm ik @@ lift1 Z.neg ik x - let add ?no_ov ik x y = norm ik @@ lift2_inj Z.add ik x y - - let sub ?no_ov ik x y = norm ik @@ lift2_inj Z.sub ik x y - let mul ?no_ov ik x y = norm ik @@ match x, y with - | `Definite z, (`Excluded _ | `Definite _) when Z.equal z Z.zero -> x - | (`Excluded _ | `Definite _), `Definite z when Z.equal z Z.zero -> y - | `Definite a, `Excluded (s,r) - (* Integer multiplication with even numbers is not injective. *) - (* Thus we cannot exclude the values to which the exclusion set would be mapped to. *) - | `Excluded (s,r),`Definite a when Z.equal (Z.rem a (Z.of_int 2)) Z.zero -> `Excluded (S.empty (), apply_range (Z.mul a) r) - | _ -> lift2_inj Z.mul ik x y - let div ?no_ov ik x y = lift2 Z.div ik x y - let rem ik x y = lift2 Z.rem ik x y - - (* Comparison handling copied from Enums. *) - let handle_bot x y f = match x, y with - | `Bot, `Bot -> `Bot - | `Bot, _ - | _, `Bot -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> f () - - let lt ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 < 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 >= 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let gt ik x y = lt ik y x - - let le ik x y = - handle_bot x y (fun () -> - match minimal x, maximal x, minimal y, maximal y with - | _, Some x2, Some y1, _ when Z.compare x2 y1 <= 0 -> of_bool ik true - | Some x1, _, _, Some y2 when Z.compare x1 y2 > 0 -> of_bool ik false - | _, _, _, _ -> top_bool) - - let ge ik x y = le ik y x - - let lognot = lift1 Z.lognot - - let logand ik x y = norm ik (match x,y with - (* We don't bother with exclusion sets: *) - | `Excluded _, `Definite i -> - (* Except in two special cases *) - if Z.equal i Z.zero then - `Definite Z.zero - else if Z.equal i Z.one then - of_interval IBool (Z.zero, Z.one) - else - top () - | `Definite _, `Excluded _ - | `Excluded _, `Excluded _ -> top () - (* The good case: *) - | `Definite x, `Definite y -> - (try `Definite (Z.logand x y) with | Division_by_zero -> top ()) - | `Bot, `Bot -> `Bot - | _ -> - (* If only one of them is bottom, we raise an exception that eval_rv will catch *) - raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y)))) - - - let logor = lift2 Z.logor - let logxor = lift2 Z.logxor - - let shift (shift_op: int_t -> int -> int_t) (ik: Cil.ikind) (x: t) (y: t) = - (* BigInt only accepts int as second argument for shifts; perform conversion here *) - let shift_op_big_int a (b: int_t) = - let (b : int) = Z.to_int b in - shift_op a b - in - (* If one of the parameters of the shift is negative, the result is undefined *) - let is_negative = GobOption.for_all (fun x -> Z.lt x Z.zero) in - if is_negative (minimal x) || is_negative (minimal y) then - top_of ik - else - norm ik @@ lift2 shift_op_big_int ik x y - - let shift_left = - shift Z.shift_left - - let shift_right = - shift Z.shift_right - (* TODO: lift does not treat Not {0} as true. *) - let c_logand ik x y = - match to_bool x, to_bool y with - | Some false, _ - | _, Some false -> - of_bool ik false - | _, _ -> - lift2 IntOps.BigIntOps.c_logand ik x y - let c_logor ik x y = - match to_bool x, to_bool y with - | Some true, _ - | _, Some true -> - of_bool ik true - | _, _ -> - lift2 IntOps.BigIntOps.c_logor ik x y - let c_lognot ik = eq ik (of_int ik Z.zero) - - let invariant_ikind e ik (x:t) = - match x with - | `Definite x -> - IntInvariant.of_int e ik x - | `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 ri = IntInvariant.of_interval e ik (rmin, rmax) in - let si = IntInvariant.of_excl_list e ik (S.elements s) in - Invariant.(ri && si) - | `Bot -> Invariant.none - - let arbitrary ik = - let open QCheck.Iter in - let excluded s = from_excl ik s in - let definite x = of_int ik x in - let shrink = function - | `Excluded (s, _) -> GobQCheck.shrink (S.arbitrary ()) s >|= excluded (* S TODO: possibly shrink excluded to definite *) - | `Definite x -> (return `Bot) <+> (GobQCheck.shrink (IntOps.BigIntOps.arbitrary ()) x >|= definite) - | `Bot -> empty - in - QCheck.frequency ~shrink ~print:show [ - 20, QCheck.map excluded (S.arbitrary ()); - 10, QCheck.map definite (IntOps.BigIntOps.arbitrary ()); - 1, QCheck.always `Bot - ] (* S TODO: decide frequencies *) - - let refine_with_congruence ik a b = a - let refine_with_interval ik a b = match a, b with - | x, Some(i) -> meet ik x (of_interval ik i) - | _ -> a - let refine_with_excl_list ik a b = match a, b with - | `Excluded (s, r), Some(ls, _) -> meet ik (`Excluded (s, r)) (of_excl_list ik ls) (* TODO: refine with excl range? *) - | _ -> a - let refine_with_incl_list ik a b = a - - let project ik p t = t -end - module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct include D From da3f5367d01bc71af446ba0a456307d69c68ed21 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:06:25 +0300 Subject: [PATCH 470/566] Rename IntDomain0 -> IntervalSetDomain for split --- .../value/cdomains/{intDomain0.ml => int/intervalSetDomain.ml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/cdomain/value/cdomains/{intDomain0.ml => int/intervalSetDomain.ml} (100%) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/int/intervalSetDomain.ml similarity index 100% rename from src/cdomain/value/cdomains/intDomain0.ml rename to src/cdomain/value/cdomains/int/intervalSetDomain.ml From e0ff2239a0323c62dda30a6617ee176bb8c1abba Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:06:48 +0300 Subject: [PATCH 471/566] Remove non-IntervalSetDomain parts --- .../value/cdomains/int/intervalSetDomain.ml | 1395 +---------------- 1 file changed, 1 insertion(+), 1394 deletions(-) diff --git a/src/cdomain/value/cdomains/int/intervalSetDomain.ml b/src/cdomain/value/cdomains/int/intervalSetDomain.ml index 0bcfa6ae44..20d647ce62 100644 --- a/src/cdomain/value/cdomains/int/intervalSetDomain.ml +++ b/src/cdomain/value/cdomains/int/intervalSetDomain.ml @@ -1,1051 +1,5 @@ -open GobConfig -open GoblintCil -open Pretty -open PrecisionUtil - -module M = Messages - -let (%) = Batteries.(%) -let (|?) = Batteries.(|?) - -exception IncompatibleIKinds of string -exception Unknown -exception Error -exception ArithmeticOnIntegerBot of string - - - - -(** Define records that hold mutable variables representing different Configuration values. - * These values are used to keep track of whether or not the corresponding Config values are en-/disabled *) -type ana_int_config_values = { - mutable interval_threshold_widening : bool option; - mutable interval_narrow_by_meet : bool option; - mutable def_exc_widen_by_join : bool option; - mutable interval_threshold_widening_constants : string option; - mutable refinement : string option; -} - -let ana_int_config: ana_int_config_values = { - interval_threshold_widening = None; - interval_narrow_by_meet = None; - def_exc_widen_by_join = None; - interval_threshold_widening_constants = None; - refinement = None; -} - -let get_interval_threshold_widening () = - if ana_int_config.interval_threshold_widening = None then - ana_int_config.interval_threshold_widening <- Some (get_bool "ana.int.interval_threshold_widening"); - Option.get ana_int_config.interval_threshold_widening - -let get_interval_narrow_by_meet () = - if ana_int_config.interval_narrow_by_meet = None then - ana_int_config.interval_narrow_by_meet <- Some (get_bool "ana.int.interval_narrow_by_meet"); - Option.get ana_int_config.interval_narrow_by_meet - -let get_def_exc_widen_by_join () = - if ana_int_config.def_exc_widen_by_join = None then - ana_int_config.def_exc_widen_by_join <- Some (get_bool "ana.int.def_exc_widen_by_join"); - Option.get ana_int_config.def_exc_widen_by_join - -let get_interval_threshold_widening_constants () = - if ana_int_config.interval_threshold_widening_constants = None then - ana_int_config.interval_threshold_widening_constants <- Some (get_string "ana.int.interval_threshold_widening_constants"); - Option.get ana_int_config.interval_threshold_widening_constants - -let get_refinement () = - if ana_int_config.refinement = None then - ana_int_config.refinement <- Some (get_string "ana.int.refinement"); - Option.get ana_int_config.refinement - - - -(** Whether for a given ikind, we should compute with wrap-around arithmetic. - * Always for unsigned types, for signed types if 'sem.int.signed_overflow' is 'assume_wraparound' *) -let should_wrap ik = not (Cil.isSigned ik) || get_string "sem.int.signed_overflow" = "assume_wraparound" - -(** Whether for a given ikind, we should assume there are no overflows. - * Always false for unsigned types, true for signed types if 'sem.int.signed_overflow' is 'assume_none' *) -let should_ignore_overflow ik = Cil.isSigned ik && get_string "sem.int.signed_overflow" = "assume_none" - -let widening_thresholds = ResettableLazy.from_fun WideningThresholds.thresholds -let widening_thresholds_desc = ResettableLazy.from_fun (List.rev % WideningThresholds.thresholds) - -type overflow_info = { overflow: bool; underflow: bool;} - -let set_overflow_flag ~cast ~underflow ~overflow ik = - if !AnalysisState.executing_speculative_computations then - (* Do not produce warnings when the operations are not actually happening in code *) - () - else - let signed = Cil.isSigned ik in - if !AnalysisState.postsolving && signed && not cast then - AnalysisState.svcomp_may_overflow := true; - let sign = if signed then "Signed" else "Unsigned" in - match underflow, overflow with - | true, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190; CWE 191] "%s integer overflow and underflow" sign - | true, false -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 191] "%s integer underflow" sign - | false, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190] "%s integer overflow" sign - | false, false -> assert false - -let reset_lazy () = - ResettableLazy.reset widening_thresholds; - ResettableLazy.reset widening_thresholds_desc; - ana_int_config.interval_threshold_widening <- None; - ana_int_config.interval_narrow_by_meet <- None; - ana_int_config.def_exc_widen_by_join <- None; - ana_int_config.interval_threshold_widening_constants <- None; - ana_int_config.refinement <- None - -module type Arith = -sig - type t - val neg: t -> t - val add: t -> t -> t - val sub: t -> t -> t - val mul: t -> t -> t - val div: t -> t -> t - val rem: t -> t -> t - - val lt: t -> t -> t - val gt: t -> t -> t - val le: t -> t -> t - val ge: t -> t -> t - val eq: t -> t -> t - val ne: t -> t -> t - - val lognot: t -> t - val logand: t -> t -> t - val logor : t -> t -> t - val logxor: t -> t -> t - - val shift_left : t -> t -> t - val shift_right: t -> t -> t - - val c_lognot: t -> t - val c_logand: t -> t -> t - val c_logor : t -> t -> t +open IntDomain0 -end - -module type ArithIkind = -sig - type t - val neg: Cil.ikind -> t -> t - val add: Cil.ikind -> t -> t -> t - val sub: Cil.ikind -> t -> t -> t - val mul: Cil.ikind -> t -> t -> t - val div: Cil.ikind -> t -> t -> t - val rem: Cil.ikind -> t -> t -> t - - val lt: Cil.ikind -> t -> t -> t - val gt: Cil.ikind -> t -> t -> t - val le: Cil.ikind -> t -> t -> t - val ge: Cil.ikind -> t -> t -> t - val eq: Cil.ikind -> t -> t -> t - val ne: Cil.ikind -> t -> t -> t - - val lognot: Cil.ikind -> t -> t - val logand: Cil.ikind -> t -> t -> t - val logor : Cil.ikind -> t -> t -> t - val logxor: Cil.ikind -> t -> t -> t - - val shift_left : Cil.ikind -> t -> t -> t - val shift_right: Cil.ikind -> t -> t -> t - - val c_lognot: Cil.ikind -> t -> t - val c_logand: Cil.ikind -> t -> t -> t - val c_logor : Cil.ikind -> t -> t -> t - -end - -(* Shared functions between S and Z *) -module type B = -sig - include Lattice.S - type int_t - val bot_of: Cil.ikind -> t - val top_of: Cil.ikind -> t - val to_int: t -> int_t option - val equal_to: int_t -> t -> [`Eq | `Neq | `Top] - - val to_bool: t -> bool option - val to_excl_list: t -> (int_t list * (int64 * int64)) option - val of_excl_list: Cil.ikind -> int_t list -> t - val is_excl_list: t -> bool - - val to_incl_list: t -> int_t list option - - val maximal : t -> int_t option - val minimal : t -> int_t option - - val cast_to: ?suppress_ovwarn:bool -> ?torg:Cil.typ -> Cil.ikind -> t -> t -end - -(** Interface of IntDomain implementations that do not take ikinds for arithmetic operations yet. TODO: Should be ported to S in the future. *) -module type IkindUnawareS = -sig - include B - include Arith with type t := t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: int_t -> t - val of_bool: bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val arbitrary: unit -> t QCheck.arbitrary - val invariant: Cil.exp -> t -> Invariant.t -end - -(** Interface of IntDomain implementations taking an ikind for arithmetic operations *) -module type S = -sig - include B - include ArithIkind with type t:= t - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val neg : ?no_ov:bool -> Cil.ikind -> t -> t - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t - - val join: Cil.ikind -> t -> t -> t - val meet: Cil.ikind -> t -> t -> t - val narrow: Cil.ikind -> t -> t -> t - val widen: Cil.ikind -> t -> t -> t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val is_top_of: Cil.ikind -> t -> bool - val invariant_ikind : Cil.exp -> Cil.ikind -> t -> Invariant.t - - val refine_with_congruence: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_interval: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_excl_list: Cil.ikind -> t -> (int_t list * (int64 * int64)) option -> t - val refine_with_incl_list: Cil.ikind -> t -> int_t list option -> t - - val project: Cil.ikind -> int_precision -> t -> t - val arbitrary: Cil.ikind -> t QCheck.arbitrary -end - -module type SOverflow = -sig - - include S - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val neg : ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val of_int : Cil.ikind -> int_t -> t * overflow_info - - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t * overflow_info - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - - val shift_left : Cil.ikind -> t -> t -> t * overflow_info - - val shift_right : Cil.ikind -> t -> t -> t * overflow_info -end - -module type Y = -sig - (* include B *) - include B - include Arith with type t:= t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val is_top_of: Cil.ikind -> t -> bool - - val project: int_precision -> t -> t - val invariant: Cil.exp -> t -> Invariant.t -end - -module type Z = Y with type int_t = Z.t - - -module IntDomLifter (I : S) = -struct - open Cil - 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 (GobPretty.sprintf "ikinds %a and %a are incompatible. Values: %a and %a" CilType.Ikind.pretty x.ikind CilType.Ikind.pretty y.ikind I.pretty x.v I.pretty y.v)) - let lift op x = {x with v = op x.ikind x.v } - (* For logical operations the result is of type int *) - let lift_logical op x = {v = op x.ikind x.v; ikind = Cil.IInt} - let lift2 op x y = check_ikinds x y; {x with v = op x.ikind x.v y.v } - let lift2_cmp op x y = check_ikinds x y; {v = op x.ikind x.v y.v; ikind = Cil.IInt} - - let bot_of ikind = { v = I.bot_of ikind; ikind} - let bot () = failwith "bot () is not implemented for IntDomLifter." - let is_bot x = I.is_bot x.v - let top_of ikind = { v = I.top_of ikind; ikind} - let top () = failwith "top () is not implemented for IntDomLifter." - let is_top x = I.is_top x.v - - (* Leq does not check for ikind, because it is used in invariant with arguments of different type. - TODO: check ikinds here and fix invariant to work with right ikinds *) - let leq x y = I.leq x.v y.v - let join = lift2 I.join - let meet = lift2 I.meet - let widen = lift2 I.widen - let narrow = lift2 I.narrow - - let show x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - "⊤" - else - I.show x.v (* TODO add ikind to output *) - let pretty () x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - Pretty.text "⊤" - else - I.pretty () x.v (* TODO add ikind to output *) - let pretty_diff () (x, y) = I.pretty_diff () (x.v, y.v) (* TODO check ikinds, add them to output *) - let printXml o x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - BatPrintf.fprintf o "\n\n⊤\n\n\n" - else - I.printXml o x.v (* TODO add ikind to output *) - (* This is for debugging *) - let name () = "IntDomLifter(" ^ (I.name ()) ^ ")" - let to_yojson x = I.to_yojson x.v - let invariant e x = - let e' = Cilfacade.mkCast ~e ~newt:(TInt (x.ikind, [])) in - I.invariant_ikind e' x.ikind x.v - let tag x = I.tag x.v - let arbitrary ik = failwith @@ "Arbitrary not implement for " ^ (name ()) ^ "." - let to_int x = I.to_int x.v - let of_int ikind x = { v = I.of_int ikind x; ikind} - let equal_to i x = I.equal_to i x.v - let to_bool x = I.to_bool x.v - let of_bool ikind b = { v = I.of_bool ikind b; ikind} - let to_excl_list x = I.to_excl_list x.v - let of_excl_list ikind is = {v = I.of_excl_list ikind is; ikind} - let is_excl_list x = I.is_excl_list x.v - let to_incl_list x = I.to_incl_list x.v - let of_interval ?(suppress_ovwarn=false) ikind (lb,ub) = {v = I.of_interval ~suppress_ovwarn ikind (lb,ub); ikind} - let of_congruence ikind (c,m) = {v = I.of_congruence ikind (c,m); ikind} - let starting ?(suppress_ovwarn=false) ikind i = {v = I.starting ~suppress_ovwarn ikind i; ikind} - let ending ?(suppress_ovwarn=false) ikind i = {v = I.ending ~suppress_ovwarn ikind i; ikind} - let maximal x = I.maximal x.v - let minimal x = I.minimal x.v - - let neg = lift I.neg - let add = lift2 I.add - let sub = lift2 I.sub - let mul = lift2 I.mul - let div = lift2 I.div - let rem = lift2 I.rem - let lt = lift2_cmp I.lt - let gt = lift2_cmp I.gt - let le = lift2_cmp I.le - let ge = lift2_cmp I.ge - let eq = lift2_cmp I.eq - let ne = lift2_cmp I.ne - let lognot = lift I.lognot - let logand = lift2 I.logand - let logor = lift2 I.logor - let logxor = lift2 I.logxor - let shift_left x y = {x with v = I.shift_left x.ikind x.v y.v } (* TODO check ikinds*) - let shift_right x y = {x with v = I.shift_right x.ikind x.v y.v } (* TODO check ikinds*) - let c_lognot = lift_logical I.c_lognot - let c_logand = lift2 I.c_logand - let c_logor = lift2 I.c_logor - - let cast_to ?(suppress_ovwarn=false) ?torg ikind x = {v = I.cast_to ~suppress_ovwarn ~torg:(TInt(x.ikind,[])) ikind x.v; ikind} - - let is_top_of ik x = ik = x.ikind && I.is_top_of ik x.v - - let relift x = { v = I.relift x.v; ikind = x.ikind } - - let project p v = { v = I.project v.ikind p v.v; ikind = v.ikind } -end - -module type Ikind = -sig - val ikind: unit -> Cil.ikind -end - -module PtrDiffIkind : Ikind = -struct - let ikind = Cilfacade.ptrdiff_ikind -end - -module IntDomWithDefaultIkind (I: Y) (Ik: Ikind) : Y with type t = I.t and type int_t = I.int_t = -struct - include I - let top () = I.top_of (Ik.ikind ()) - let bot () = I.bot_of (Ik.ikind ()) -end - -module Size = struct (* size in bits as int, range as int64 *) - open Cil - let sign x = if Z.compare x Z.zero < 0 then `Signed else `Unsigned - - let top_typ = TInt (ILongLong, []) - let min_for x = intKindForValue x (sign x = `Unsigned) - let bit = function (* bits needed for representation *) - | IBool -> 1 - | ik -> bytesSizeOfInt ik * 8 - let is_int64_big_int x = Z.fits_int64 x - let card ik = (* cardinality *) - let b = bit ik in - Z.shift_left Z.one b - let bits ik = (* highest bits for neg/pos values *) - let s = bit ik in - if isSigned ik then s-1, s-1 else 0, s - let bits_i64 ik = BatTuple.Tuple2.mapn Int64.of_int (bits ik) - let range ik = - let a,b = bits ik in - let x = if isSigned ik then Z.neg (Z.shift_left Z.one a) (* -2^a *) else Z.zero in - let y = Z.pred (Z.shift_left Z.one b) in (* 2^b - 1 *) - x,y - - let is_cast_injective ~from_type ~to_type = - let (from_min, from_max) = range (Cilfacade.get_ikind from_type) in - let (to_min, to_max) = range (Cilfacade.get_ikind to_type) in - if M.tracing then M.trace "int" "is_cast_injective %a (%a, %a) -> %a (%a, %a)" CilType.Typ.pretty from_type GobZ.pretty from_min GobZ.pretty from_max CilType.Typ.pretty to_type GobZ.pretty to_min GobZ.pretty to_max; - Z.compare to_min from_min <= 0 && Z.compare from_max to_max <= 0 - - let cast t x = (* TODO: overflow is implementation-dependent! *) - if t = IBool then - (* C11 6.3.1.2 Boolean type *) - if Z.equal x Z.zero then Z.zero else Z.one - else - let a,b = range t in - let c = card t in - let y = Z.erem x c in - let y = if Z.gt y b then Z.sub y c - else if Z.lt y a then Z.add y c - else y - in - if M.tracing then M.tracel "cast" "Cast %a to range [%a, %a] (%a) = %a (%s in int64)" GobZ.pretty x GobZ.pretty a GobZ.pretty b GobZ.pretty c GobZ.pretty y (if is_int64_big_int y then "fits" else "does not fit"); - y - - let min_range_sign_agnostic x = - let size ik = - let a,b = bits_i64 ik in - Int64.neg a,b - in - if sign x = `Signed then - size (min_for x) - else - let a, b = size (min_for x) in - if b <= 64L then - let upper_bound_less = Int64.sub b 1L in - let max_one_less = Z.(pred @@ shift_left Z.one (Int64.to_int upper_bound_less)) in - if x <= max_one_less then - a, upper_bound_less - else - a,b - else - a, b - - (* From the number of bits used to represent a positive value, determines the maximal representable value *) - let max_from_bit_range pos_bits = Z.(pred @@ shift_left Z.one (to_int (Z.of_int64 pos_bits))) - - (* From the number of bits used to represent a non-positive value, determines the minimal representable value *) - let min_from_bit_range neg_bits = Z.(if neg_bits = 0L then Z.zero else neg @@ shift_left Z.one (to_int (neg (Z.of_int64 neg_bits)))) - -end - - -module StdTop (B: sig type t val top_of: Cil.ikind -> t end) = struct - open B - (* these should be overwritten for better precision if possible: *) - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ik x = top_of ik - let ending ?(suppress_ovwarn=false) ik x = top_of ik - let maximal x = None - let minimal x = None -end - -module Std (B: sig - type t - val name: unit -> string - val top_of: Cil.ikind -> t - val bot_of: Cil.ikind -> t - val show: t -> string - val equal: t -> t -> bool - end) = struct - include Printable.StdLeaf - let name = B.name (* overwrite the one from Printable.Std *) - open B - let is_top x = failwith "is_top not implemented for IntDomain.Std" - let is_bot x = B.equal x (bot_of Cil.IInt) (* Here we assume that the representation of bottom is independent of the ikind - This may be true for intdomain implementations, but not e.g. for IntDomLifter. *) - let is_top_of ik x = B.equal x (top_of ik) - - (* all output is based on B.show *) - include Printable.SimpleShow ( - struct - type nonrec t = t - let show = show - end - ) - let pretty_diff () (x,y) = dprintf "%s: %a instead of %a" (name ()) pretty x pretty y - - include StdTop (B) -end - -(* Textbook interval arithmetic, without any overflow handling etc. *) -module IntervalArith (Ints_t : IntOps.IntOps) = struct - let min4 a b c d = Ints_t.min (Ints_t.min a b) (Ints_t.min c d) - let max4 a b c d = Ints_t.max (Ints_t.max a b) (Ints_t.max c d) - - let mul (x1, x2) (y1, y2) = - let x1y1 = (Ints_t.mul x1 y1) in - let x1y2 = (Ints_t.mul x1 y2) in - let x2y1 = (Ints_t.mul x2 y1) in - let x2y2 = (Ints_t.mul x2 y2) in - (min4 x1y1 x1y2 x2y1 x2y2, max4 x1y1 x1y2 x2y1 x2y2) - - let shift_left (x1,x2) (y1,y2) = - let y1p = Ints_t.shift_left Ints_t.one y1 in - let y2p = Ints_t.shift_left Ints_t.one y2 in - mul (x1, x2) (y1p, y2p) - - let div (x1, x2) (y1, y2) = - let x1y1n = (Ints_t.div x1 y1) in - let x1y2n = (Ints_t.div x1 y2) in - let x2y1n = (Ints_t.div x2 y1) in - let x2y2n = (Ints_t.div x2 y2) in - let x1y1p = (Ints_t.div x1 y1) in - let x1y2p = (Ints_t.div x1 y2) in - let x2y1p = (Ints_t.div x2 y1) in - let x2y2p = (Ints_t.div x2 y2) in - (min4 x1y1n x1y2n x2y1n x2y2n, max4 x1y1p x1y2p x2y1p x2y2p) - - let add (x1, x2) (y1, y2) = (Ints_t.add x1 y1, Ints_t.add x2 y2) - let sub (x1, x2) (y1, y2) = (Ints_t.sub x1 y2, Ints_t.sub x2 y1) - - let neg (x1, x2) = (Ints_t.neg x2, Ints_t.neg x1) - - let one = (Ints_t.one, Ints_t.one) - let zero = (Ints_t.zero, Ints_t.zero) - let top_bool = (Ints_t.zero, Ints_t.one) - - let to_int (x1, x2) = - if Ints_t.equal x1 x2 then Some x1 else None - - let upper_threshold u max_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - let max_ik' = Ints_t.to_bigint max_ik in - let t = List.find_opt (fun x -> Z.compare u x <= 0 && Z.compare x max_ik' <= 0) ts in - BatOption.map_default Ints_t.of_bigint max_ik t - let lower_threshold l min_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - let min_ik' = Ints_t.to_bigint min_ik in - let t = List.find_opt (fun x -> Z.compare l x >= 0 && Z.compare x min_ik' >= 0) ts in - BatOption.map_default Ints_t.of_bigint min_ik t - let is_upper_threshold u = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - List.exists (Z.equal u) ts - let is_lower_threshold l = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - List.exists (Z.equal l) ts -end - -module IntInvariant = -struct - let of_int e ik x = - if get_bool "witness.invariant.exact" then - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) - else - Invariant.none - - let of_incl_list e ik ps = - match ps with - | [_; _] when ik = IBool && not (get_bool "witness.invariant.inexact-type-bounds") -> - assert (List.mem Z.zero ps); - assert (List.mem Z.one ps); - Invariant.none - | [_] when get_bool "witness.invariant.exact" -> - Invariant.none - | _ :: _ :: _ - | [_] | [] -> - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in - Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) (Invariant.bot ()) ps - - let of_interval_opt e ik = function - | (Some x1, Some x2) when Z.equal x1 x2 -> - of_int e ik x1 - | x1_opt, x2_opt -> - let (min_ik, max_ik) = Size.range ik in - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in - let i1 = - match x1_opt, inexact_type_bounds with - | Some x1, false when Z.equal min_ik x1 -> Invariant.none - | Some x1, _ -> Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1, e, intType)) - | None, _ -> Invariant.none - in - let i2 = - match x2_opt, inexact_type_bounds with - | Some x2, false when Z.equal x2 max_ik -> Invariant.none - | Some x2, _ -> Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) - | None, _ -> Invariant.none - in - Invariant.(i1 && i2) - - let of_interval e ik (x1, x2) = - of_interval_opt e ik (Some x1, Some x2) - - let of_excl_list e ik ns = - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in - Invariant.(a && i) - ) (Invariant.top ()) ns -end - -module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = -struct - let name () = "intervals" - type int_t = Ints_t.t - type t = (Ints_t.t * Ints_t.t) option [@@deriving eq, ord, hash] - module IArith = IntervalArith (Ints_t) - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - let top_of ik = Some (range ik) - let bot () = None - let bot_of ik = bot () (* TODO: improve *) - - let show = function None -> "bottom" | Some (x,y) -> "["^Ints_t.to_string x^","^Ints_t.to_string y^"]" - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let equal_to i = function - | None -> failwith "unsupported: equal_to with bottom" - | Some (a, b) -> - if a = b && b = i then `Eq else if Ints_t.compare a i <= 0 && Ints_t.compare i b <=0 then `Top else `Neq - - let norm ?(suppress_ovwarn=false) ?(cast=false) ik : (t -> t * overflow_info) = function None -> (None, {underflow=false; overflow=false}) | Some (x,y) -> - if Ints_t.compare x y > 0 then - (None,{underflow=false; overflow=false}) - else ( - let (min_ik, max_ik) = range ik in - let underflow = Ints_t.compare min_ik x > 0 in - let overflow = Ints_t.compare max_ik y < 0 in - let ov_info = { underflow = underflow && not suppress_ovwarn; overflow = overflow && not suppress_ovwarn } in - let v = - if underflow || overflow then - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (Ints_t.sub max_ik min_ik) in - let resdiff = Ints_t.abs (Ints_t.sub y x) in - if Ints_t.compare resdiff diff > 0 then - top_of ik - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if Ints_t.compare l u <= 0 then - Some (l, u) - else - (* Interval that wraps around (begins to the right of its end). We can not represent such intervals *) - top_of ik - else if not cast && should_ignore_overflow ik then - let tl, tu = BatOption.get @@ top_of ik in - Some (Ints_t.max tl x, Ints_t.min tu y) - else - top_of ik - else - Some (x,y) - in - (v, ov_info) - ) - - let leq (x:t) (y:t) = - match x, y with - | None, _ -> true - | Some _, None -> false - | Some (x1,x2), Some (y1,y2) -> Ints_t.compare x1 y1 >= 0 && Ints_t.compare x2 y2 <= 0 - - let join ik (x:t) y = - match x, y with - | None, z | z, None -> z - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.min x1 y1, Ints_t.max x2 y2) |> fst - - let meet ik (x:t) y = - match x, y with - | None, z | z, None -> None - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.max x1 y1, Ints_t.min x2 y2) |> fst - - (* TODO: change to_int signature so it returns a big_int *) - let to_int x = Option.bind x (IArith.to_int) - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm ~suppress_ovwarn ik @@ Some (x,y) - let of_int ik (x: int_t) = of_interval ik (x,x) - let zero = Some IArith.zero - let one = Some IArith.one - let top_bool = Some IArith.top_bool - - let of_bool _ik = function true -> one | false -> zero - let to_bool (a: t) = match a with - | None -> None - | Some (l, u) when Ints_t.compare l Ints_t.zero = 0 && Ints_t.compare u Ints_t.zero = 0 -> Some false - | x -> if leq zero x then None else Some true - - let starting ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (fst (range ik), n) - - (* TODO: change signature of maximal, minimal to return big_int*) - let maximal = function None -> None | Some (x,y) -> Some y - let minimal = function None -> None | Some (x,y) -> Some x - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov t = norm ~cast:true t (* norm does all overflow handling *) - - let widen ik x y = - match x, y with - | None, z | z, None -> z - | Some (l0,u0), Some (l1,u1) -> - let (min_ik, max_ik) = range ik in - let threshold = get_interval_threshold_widening () in - let l2 = - if Ints_t.compare l0 l1 = 0 then l0 - else if threshold then IArith.lower_threshold l1 min_ik - else min_ik - in - let u2 = - if Ints_t.compare u0 u1 = 0 then u0 - else if threshold then IArith.upper_threshold u1 max_ik - else max_ik - in - norm ik @@ Some (l2,u2) |> fst - let widen ik x y = - let r = widen ik x y in - if M.tracing && not (equal x y) then M.tracel "int" "interval widen %a %a -> %a" pretty x pretty y pretty r; - assert (leq x y); (* TODO: remove for performance reasons? *) - r - - let narrow ik x y = - match x, y with - | _,None | None, _ -> None - | Some (x1,x2), Some (y1,y2) -> - let threshold = get_interval_threshold_widening () in - let (min_ik, max_ik) = range ik in - let lr = if Ints_t.compare min_ik x1 = 0 || threshold && Ints_t.compare y1 x1 > 0 && IArith.is_lower_threshold x1 then y1 else x1 in - let ur = if Ints_t.compare max_ik x2 = 0 || threshold && Ints_t.compare y2 x2 < 0 && IArith.is_upper_threshold x2 then y2 else x2 in - norm ik @@ Some (lr,ur) |> fst - - - let narrow ik x y = - if get_interval_narrow_by_meet () then - meet ik x y - else - narrow ik x y - - let log f ~annihilator ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_bool i1, to_bool i2 with - | Some x, _ when x = annihilator -> of_bool ik annihilator - | _, Some y when y = annihilator -> of_bool ik annihilator - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - let c_logor = log (||) ~annihilator:true - let c_logand = log (&&) ~annihilator:false - - let log1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_bool i1 with - | Some x -> of_bool ik (f ik x) - | _ -> top_of ik - - let c_lognot = log1 (fun _ik -> not) - - let bit f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - let bitcomp f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{underflow=false; overflow=false})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let logxor = bit (fun _ik -> Ints_t.logxor) - - let logand ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (Ints_t.logand x y) |> fst with Division_by_zero -> top_of ik) - | _, Some y when Ints_t.equal y Ints_t.zero -> of_int ik Ints_t.zero |> fst - | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst - | _ -> top_of ik - - let logor = bit (fun _ik -> Ints_t.logor) - - let bit1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_int i1 with - | Some x -> of_int ik (f ik x) |> fst - | _ -> top_of ik - - let lognot = bit1 (fun _ik -> Ints_t.lognot) - let shift_right = bitcomp (fun _ik x y -> Ints_t.shift_right x (Ints_t.to_int y)) - - let neg ?no_ov ik = function None -> (None,{underflow=false; overflow=false}) | Some x -> norm ik @@ Some (IArith.neg x) - - let binary_op_with_norm ?no_ov op ik x y = match x, y with - | None, None -> (None, {overflow=false; underflow= false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some x, Some y -> norm ik @@ Some (op x y) - - let add ?no_ov = binary_op_with_norm IArith.add - let mul ?no_ov = binary_op_with_norm IArith.mul - let sub ?no_ov = binary_op_with_norm IArith.sub - - let shift_left ik a b = - match is_bot a, is_bot b with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show a) (show b))) - | _ -> - match a, minimal b, maximal b with - | Some a, Some bl, Some bu when (Ints_t.compare bl Ints_t.zero >= 0) -> - (try - let r = IArith.shift_left a (Ints_t.to_int bl, Ints_t.to_int bu) in - norm ik @@ Some r - with Z.Overflow -> (top_of ik,{underflow=false; overflow=true})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let rem ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (xl, xu), Some (yl, yu) -> - if is_top_of ik x && is_top_of ik y then - (* This is needed to preserve soundness also on things bigger than int32 e.g. *) - (* x: 3803957176L -> T in Interval32 *) - (* y: 4209861404L -> T in Interval32 *) - (* x % y: 3803957176L -> T in Interval32 *) - (* T in Interval32 is [-2147483648,2147483647] *) - (* the code below computes [-2147483647,2147483647] for this though which is unsound *) - top_of ik - else - (* If we have definite values, Ints_t.rem will give a definite result. - * Otherwise we meet with a [range] the result can be in. - * This range is [0, min xu b] if x is positive, and [max xl -b, min xu b] if x can be negative. - * The precise bound b is one smaller than the maximum bound. Negative y give the same result as positive. *) - let pos x = if Ints_t.compare x Ints_t.zero < 0 then Ints_t.neg x else x in - let b = Ints_t.sub (Ints_t.max (pos yl) (pos yu)) Ints_t.one in - let range = if Ints_t.compare xl Ints_t.zero>= 0 then Some (Ints_t.zero, Ints_t.min xu b) else Some (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit (fun _ik -> Ints_t.rem) ik x y) range - - let rec div ?no_ov ik x y = - match x, y with - | None, None -> (bot (),{underflow=false; overflow=false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | (Some (x1,x2) as x), (Some (y1,y2) as y) -> - begin - let is_zero v = Ints_t.compare v Ints_t.zero = 0 in - match y1, y2 with - | l, u when is_zero l && is_zero u -> (top_of ik,{underflow=false; overflow=false}) (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> div ik (Some (x1,x2)) (Some (Ints_t.one,y2)) - | _, u when is_zero u -> div ik (Some (x1,x2)) (Some (y1, Ints_t.(neg one))) - | _ when leq (of_int ik (Ints_t.zero) |> fst) (Some (y1,y2)) -> (top_of ik,{underflow=false; overflow=false}) - | _ -> binary_op_with_norm IArith.div ik x y - end - - let ne ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik true - else if Ints_t.compare x2 y1 <= 0 && Ints_t.compare y2 x1 <= 0 then - of_bool ik false - else top_bool - - let eq ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 && Ints_t.compare x2 y1 <= 0 then - of_bool ik true - else if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik false - else top_bool - - let ge ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 then of_bool ik true - else if Ints_t.compare x2 y1 < 0 then of_bool ik false - else top_bool - - let le ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 <= 0 then of_bool ik true - else if Ints_t.compare y2 x1 < 0 then of_bool ik false - else top_bool - - let gt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 then of_bool ik true - else if Ints_t.compare x2 y1 <= 0 then of_bool ik false - else top_bool - - let lt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 < 0 then of_bool ik true - else if Ints_t.compare y2 x1 <= 0 then of_bool ik false - else top_bool - - let invariant_ikind e ik = function - | Some (x1, x2) -> - let (x1', x2') = BatTuple.Tuple2.mapn Ints_t.to_bigint (x1, x2) in - IntInvariant.of_interval e ik (x1', x2') - | None -> Invariant.none - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let shrink = function - | Some (l, u) -> (return None) <+> (GobQCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) - | None -> empty - in - QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) (fun x -> of_interval ik x |> fst ) pair_arb) - - let modulo n k = - let result = Ints_t.rem n k in - if Ints_t.compare result Ints_t.zero >= 0 then result - else Ints_t.add result k - - let refine_with_congruence ik (intv : t) (cong : (int_t * int_t ) option) : t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if Ints_t.equal m Ints_t.zero && (Ints_t.compare c x < 0 || Ints_t.compare c y > 0) then None - else if Ints_t.equal m Ints_t.zero then - Some (c, c) - else - let (min_ik, max_ik) = range ik in - let rcx = - if Ints_t.equal x min_ik then x else - Ints_t.add x (modulo (Ints_t.sub c x) (Ints_t.abs m)) in - let lcy = - if Ints_t.equal y max_ik then y else - Ints_t.sub y (modulo (Ints_t.sub y c) (Ints_t.abs m)) in - if Ints_t.compare rcx lcy > 0 then None - else if Ints_t.equal rcx lcy then norm ik @@ Some (rcx, rcx) |> fst - else norm ik @@ Some (rcx, lcy) |> fst - | _ -> None - - let refine_with_congruence ik x y = - let refn = refine_with_congruence ik x y in - if M.tracing then M.trace "refine" "int_refine_with_congruence %a %a -> %a" pretty x pretty y pretty refn; - refn - - let refine_with_interval ik a b = meet ik a b - - let refine_with_excl_list ik (intv : t) (excl : (int_t list * (int64 * int64)) option) : t = - match intv, excl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls, (rl, rh)) -> - let rec shrink op b = - let new_b = (op b (Ints_t.of_int(Bool.to_int(BatList.mem_cmp Ints_t.compare b ls)))) in - if not (Ints_t.equal b new_b) then shrink op new_b else new_b - in - let (min_ik, max_ik) = range ik in - let l' = if Ints_t.equal l min_ik then l else shrink Ints_t.add l in - let u' = if Ints_t.equal u max_ik then u else shrink Ints_t.sub u in - let intv' = norm ik @@ Some (l', u') |> fst in - let range = norm ~suppress_ovwarn:true ik (Some (Ints_t.of_bigint (Size.min_from_bit_range rl), Ints_t.of_bigint (Size.max_from_bit_range rh))) |> fst in - meet ik intv' range - - let refine_with_incl_list ik (intv: t) (incl : (int_t list) option) : t = - match intv, incl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls) -> - let rec min m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> min (Some x) xs | Some m -> if Ints_t.compare m x < 0 then min (Some m) xs else min (Some x) xs in - let rec max m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> max (Some x) xs | Some m -> if Ints_t.compare m x > 0 then max (Some m) xs else max (Some x) xs in - match min None ls, max None ls with - | Some m1, Some m2 -> refine_with_interval ik (Some(l, u)) (Some (m1, m2)) - | _, _-> intv - - let project ik p t = t -end (** IntervalSetFunctor that is not just disjunctive completion of intervals, but attempts to be precise for wraparound arithmetic for unsigned types *) module IntervalSetFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) list = @@ -1581,350 +535,3 @@ struct let shrink xs = GobQCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) canonize_randomly_generated_list list_pair_arb) end - -module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t = struct - include D - - let add ?no_ov ik x y = fst @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = fst @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = fst @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = fst @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = fst @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = fst @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = fst @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = fst @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = fst @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = fst @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = fst @@ D.shift_left ik x y - - let shift_right ik x y = fst @@ D.shift_right ik x y -end - -module IntIkind = struct let ikind () = Cil.IInt end -module Interval = IntervalFunctor (IntOps.BigIntOps) -module Interval32 = IntDomWithDefaultIkind (IntDomLifter (SOverflowUnlifter (IntervalFunctor (IntOps.Int64Ops)))) (IntIkind) -module IntervalSet = IntervalSetFunctor (IntOps.BigIntOps) -module Integers (Ints_t : IntOps.IntOps): IkindUnawareS with type t = Ints_t.t and type int_t = Ints_t.t = (* no top/bot, order is <= *) -struct - include Printable.Std - let name () = "integers" - type t = Ints_t.t [@@deriving eq, ord, hash] - type int_t = Ints_t.t - let top () = raise Unknown - let bot () = raise Error - let top_of ik = top () - let bot_of ik = bot () - let show (x: Ints_t.t) = Ints_t.to_string x - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - (* is_top and is_bot are never called, but if they were, the Std impl would raise their exception, so we overwrite them: *) - let is_top _ = false - let is_bot _ = false - - let equal_to i x = if i > x then `Neq else `Top - let leq x y = x <= y - let join x y = if Ints_t.compare x y > 0 then x else y - let widen = join - let meet x y = if Ints_t.compare x y > 0 then y else x - let narrow = meet - - let of_bool x = if x then Ints_t.one else Ints_t.zero - let to_bool' x = x <> Ints_t.zero - let to_bool x = Some (to_bool' x) - let of_int x = x - let to_int x = Some x - - let neg = Ints_t.neg - let add = Ints_t.add (* TODO: signed overflow is undefined behavior! *) - let sub = Ints_t.sub - let mul = Ints_t.mul - let div = Ints_t.div - let rem = Ints_t.rem - let lt n1 n2 = of_bool (n1 < n2) - let gt n1 n2 = of_bool (n1 > n2) - let le n1 n2 = of_bool (n1 <= n2) - let ge n1 n2 = of_bool (n1 >= n2) - let eq n1 n2 = of_bool (n1 = n2) - let ne n1 n2 = of_bool (n1 <> n2) - let lognot = Ints_t.lognot - let logand = Ints_t.logand - let logor = Ints_t.logor - let logxor = Ints_t.logxor - let shift_left n1 n2 = Ints_t.shift_left n1 (Ints_t.to_int n2) - let shift_right n1 n2 = Ints_t.shift_right n1 (Ints_t.to_int n2) - let c_lognot n1 = of_bool (not (to_bool' n1)) - let c_logand n1 n2 = of_bool ((to_bool' n1) && (to_bool' n2)) - let c_logor n1 n2 = of_bool ((to_bool' n1) || (to_bool' n2)) - let cast_to ?(suppress_ovwarn=false) ?torg t x = failwith @@ "Cast_to not implemented for " ^ (name ()) ^ "." - let arbitrary ik = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 (* TODO: use ikind *) - let invariant _ _ = Invariant.none (* TODO *) -end - -module FlatPureIntegers: IkindUnawareS with type t = int64 and type int_t = int64 = (* Integers, but raises Unknown/Error on join/meet *) -struct - include Integers(IntOps.Int64Ops) - let top () = raise Unknown - let bot () = raise Error - let leq = equal - let pretty_diff () (x,y) = Pretty.dprintf "Integer %a instead of %a" pretty x pretty y - let join x y = if equal x y then x else top () - let meet x y = if equal x y then x else bot () -end - -module Flat (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Lift, but goes to `Top/`Bot if Base raises Unknown/Error *) -struct - type int_t = Base.int_t - include Lattice.FlatConf (struct - include Printable.DefaultConf - let top_name = "Unknown int" - let bot_name = "Error int" - end) (Base) - - let top_of ik = top () - let bot_of ik = bot () - - - let name () = "flat integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ikind x = top_of ikind - let ending ?(suppress_ovwarn=false) ikind x = top_of ikind - let maximal x = None - let minimal x = None - - let lift1 f x = match x with - | `Lifted x -> - (try `Lifted (f x) with Unknown -> `Top | Error -> `Bot) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> - (try `Lifted (f x y) with Unknown -> `Top | Error -> `Bot) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Lift (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Flat, but does not go to `Top/Bot` if Base raises Unknown/Error *) -struct - include Lattice.LiftPO (struct - include Printable.DefaultConf - let top_name = "MaxInt" - let bot_name = "MinInt" - end) (Base) - type int_t = Base.int_t - let top_of ik = top () - let bot_of ik = bot () - include StdTop (struct type nonrec t = t let top_of = top_of end) - - let name () = "lifted integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let lift1 f x = match x with - | `Lifted x -> `Lifted (f x) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> `Lifted (f x y) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Flattened = Flat (Integers (IntOps.Int64Ops)) -module Lifted = Lift (Integers (IntOps.Int64Ops)) - -module Reverse (Base: IkindUnawareS) = -struct - include Base - include (Lattice.Reverse (Base) : Lattice.S with type t := Base.t) -end - -module BISet = struct - include SetDomain.Make (IntOps.BigIntOps) - let is_singleton s = cardinal s = 1 -end - -(* The module [Exclusion] constains common functionality about handling of exclusion sets between [DefExc] and [Enums] *) -module Exclusion = -struct - module R = Interval32 - (* We use these types for the functions in this module to make the intended meaning more explicit *) - type t = Exc of BISet.t * Interval32.t - type inc = Inc of BISet.t [@@unboxed] - let max_of_range r = Size.max_from_bit_range (Option.get (R.maximal r)) - let min_of_range r = Size.min_from_bit_range (Option.get (R.minimal r)) - let cardinality_of_range r = Z.succ (Z.add (Z.neg (min_of_range r)) (max_of_range r)) - - let cardinality_BISet s = - Z.of_int (BISet.cardinal s) - - let leq_excl_incl (Exc (xs, r)) (Inc ys) = - (* For a <= b to hold, the cardinalities must fit, i.e. |a| <= |b|, which implies |min_r, max_r| - |xs| <= |ys|. We check this first. *) - let lower_bound_cardinality_a = Z.sub (cardinality_of_range r) (cardinality_BISet xs) in - let card_b = cardinality_BISet ys in - if Z.compare lower_bound_cardinality_a card_b > 0 then - false - else (* The cardinality did fit, so we check for all elements that are represented by range r, whether they are in (xs union ys) *) - let min_a = min_of_range r in - let max_a = max_of_range r in - GobZ.for_all_range (fun el -> BISet.mem el xs || BISet.mem el ys) (min_a, max_a) - - let leq (Exc (xs, r)) (Exc (ys, s)) = - let min_a, max_a = min_of_range r, max_of_range r in - let excluded_check = BISet.for_all (fun y -> BISet.mem y xs || Z.compare y min_a < 0 || Z.compare y max_a > 0) ys in (* if true, then the values ys, that are not in b, also do not occur in a *) - if not excluded_check - then false - else begin (* Check whether all elements that are in the range r, but not in s, are in xs, i.e. excluded. *) - if R.leq r s then true - else begin if Z.compare (cardinality_BISet xs) (Z.sub (cardinality_of_range r) (cardinality_of_range s)) >= 0 (* Check whether the number of excluded elements in a is as least as big as |min_r, max_r| - |min_s, max_s| *) - then - let min_b, max_b = min_of_range s, max_of_range s in - let leq1 = (* check whether the elements in [r_l; s_l-1] are all in xs, i.e. excluded *) - if Z.compare min_a min_b < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (min_a, Z.pred min_b) - else - true - in - let leq2 () = (* check whether the elements in [s_u+1; r_u] are all in xs, i.e. excluded *) - if Z.compare max_b max_a < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (Z.succ max_b, max_a) - else - true - in - leq1 && (leq2 ()) - else - false - end - end -end - -module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct - - include D - - let lift v = (v, {overflow=false; underflow=false}) - - let add ?no_ov ik x y = lift @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = lift @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = lift @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = lift @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = lift @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = lift @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = lift @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = lift @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = lift @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = lift @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = lift @@ D.shift_left ik x y - - let shift_right ik x y = lift @@ D.shift_right ik x y - -end From 3720ea2d30ffef85a3344f7ba99e3f860e15f0ab Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:07:58 +0300 Subject: [PATCH 472/566] Remove IntervalSetFunctor from IntDomain0 --- src/cdomain/value/cdomains/intDomain0.ml | 535 ----------------------- 1 file changed, 535 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/intDomain0.ml index 0bcfa6ae44..833de85ee4 100644 --- a/src/cdomain/value/cdomains/intDomain0.ml +++ b/src/cdomain/value/cdomains/intDomain0.ml @@ -1047,541 +1047,6 @@ struct let project ik p t = t end -(** IntervalSetFunctor that is not just disjunctive completion of intervals, but attempts to be precise for wraparound arithmetic for unsigned types *) -module IntervalSetFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) list = -struct - - module Interval = IntervalFunctor (Ints_t) - module IArith = IntervalArith (Ints_t) - - - let name () = "interval_sets" - - type int_t = Ints_t.t - - let (>.) a b = Ints_t.compare a b > 0 - let (=.) a b = Ints_t.compare a b = 0 - let (<.) a b = Ints_t.compare a b < 0 - let (>=.) a b = Ints_t.compare a b >= 0 - let (<=.) a b = Ints_t.compare a b <= 0 - let (+.) a b = Ints_t.add a b - let (-.) a b = Ints_t.sub a b - - (* - Each domain's element is guaranteed to be in canonical form. That is, each interval contained - inside the set does not overlap with each other and they are not adjacent. - *) - type t = (Ints_t.t * Ints_t.t) list [@@deriving eq, hash, ord] - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - - let top_of ik = [range ik] - - let bot () = [] - - let bot_of ik = bot () - - let show (x: t) = - let show_interval i = Printf.sprintf "[%s, %s]" (Ints_t.to_string (fst i)) (Ints_t.to_string (snd i)) in - List.fold_left (fun acc i -> (show_interval i) :: acc) [] x |> List.rev |> String.concat ", " |> Printf.sprintf "[%s]" - - (* New type definition for the sweeping line algorithm used for implementing join/meet functions. *) - type event = Enter of Ints_t.t | Exit of Ints_t.t - - let unbox_event = function Enter x -> x | Exit x -> x - - let cmp_events x y = - (* Deliberately comparing ints first => Cannot be derived *) - let res = Ints_t.compare (unbox_event x) (unbox_event y) in - if res <> 0 then res - else - begin - match (x, y) with - | (Enter _, Exit _) -> -1 - | (Exit _, Enter _) -> 1 - | (_, _) -> 0 - end - - let interval_set_to_events (xs: t) = - List.concat_map (fun (a, b) -> [Enter a; Exit b]) xs - - let two_interval_sets_to_events (xs: t) (ys: t) = - let xs = interval_set_to_events xs in - let ys = interval_set_to_events ys in - List.merge cmp_events xs ys - - (* Using the sweeping line algorithm, combined_event_list returns a new event list representing the intervals in which at least n intervals in xs overlap - This function is used for both join and meet operations with different parameter n: 1 for join, 2 for meet *) - let combined_event_list lattice_op (xs:event list) = - let l = match lattice_op with `Join -> 1 | `Meet -> 2 in - let aux (interval_count, acc) = function - | Enter x -> (interval_count + 1, if (interval_count + 1) >= l && interval_count < l then (Enter x)::acc else acc) - | Exit x -> (interval_count - 1, if interval_count >= l && (interval_count - 1) < l then (Exit x)::acc else acc) - in - List.fold_left aux (0, []) xs |> snd |> List.rev - - let rec events_to_intervals = function - | [] -> [] - | (Enter x)::(Exit y)::xs -> (x, y)::(events_to_intervals xs) - | _ -> failwith "Invalid events list" - - let remove_empty_gaps (xs: t) = - let aux acc (l, r) = match acc with - | ((a, b)::acc') when (b +. Ints_t.one) >=. l -> (a, r)::acc' - | _ -> (l, r)::acc - in - List.fold_left aux [] xs |> List.rev - - let canonize (xs: t) = - interval_set_to_events xs |> - List.sort cmp_events |> - combined_event_list `Join |> - events_to_intervals |> - remove_empty_gaps - - let unop (x: t) op = match x with - | [] -> [] - | _ -> canonize @@ List.concat_map op x - - let binop (x: t) (y: t) op : t = match x, y with - | [], _ -> [] - | _, [] -> [] - | _, _ -> canonize @@ List.concat_map op (BatList.cartesian_product x y) - - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let minimal = function - | [] -> None - | (x, _)::_ -> Some x - - let maximal = function - | [] -> None - | xs -> Some (BatList.last xs |> snd) - - let equal_to_interval i (a, b) = - if a =. b && b =. i then - `Eq - else if a <=. i && i <=. b then - `Top - else - `Neq - - let equal_to i xs = match List.map (equal_to_interval i) xs with - | [] -> failwith "unsupported: equal_to with bottom" - | [`Eq] -> `Eq - | ys when List.for_all ((=) `Neq) ys -> `Neq - | _ -> `Top - - let norm_interval ?(suppress_ovwarn=false) ?(cast=false) ik (x,y) : t*overflow_info = - if x >. y then - ([],{underflow=false; overflow=false}) - else - let (min_ik, max_ik) = range ik in - let underflow = min_ik >. x in - let overflow = max_ik <. y in - let v = if underflow || overflow then - begin - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (max_ik -. min_ik) in - let resdiff = Ints_t.abs (y -. x) in - if resdiff >. diff then - [range ik] - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if l <=. u then - [(l, u)] - else - (* Interval that wraps around (begins to the right of its end). We CAN represent such intervals *) - [(min_ik, u); (l, max_ik)] - else if not cast && should_ignore_overflow ik then - [Ints_t.max min_ik x, Ints_t.min max_ik y] - else - [range ik] - end - else - [(x,y)] - in - if suppress_ovwarn then (v, {underflow=false; overflow=false}) else (v, {underflow; overflow}) - - let norm_intvs ?(suppress_ovwarn=false) ?(cast=false) (ik:ikind) (xs: t) : t*overflow_info = - let res = List.map (norm_interval ~suppress_ovwarn ~cast ik) xs in - let intvs = List.concat_map fst res in - let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in - let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in - (canonize intvs,{underflow; overflow}) - - let binary_op_with_norm op (ik:ikind) (x: t) (y: t) : t*overflow_info = match x, y with - | [], _ -> ([],{overflow=false; underflow=false}) - | _, [] -> ([],{overflow=false; underflow=false}) - | _, _ -> norm_intvs ik @@ List.concat_map (fun (x,y) -> [op x y]) (BatList.cartesian_product x y) - - let binary_op_with_ovc (x: t) (y: t) op : t*overflow_info = match x, y with - | [], _ -> ([],{overflow=false; underflow=false}) - | _, [] -> ([],{overflow=false; underflow=false}) - | _, _ -> - let res = List.map op (BatList.cartesian_product x y) in - let intvs = List.concat_map fst res in - let underflow = List.exists (fun (_,{underflow; _}) -> underflow) res in - let overflow = List.exists (fun (_,{overflow; _}) -> underflow) res in - (canonize intvs,{underflow; overflow}) - - let unary_op_with_norm op (ik:ikind) (x: t) = match x with - | [] -> ([],{overflow=false; underflow=false}) - | _ -> norm_intvs ik @@ List.concat_map (fun x -> [op x]) x - - let rec leq (xs: t) (ys: t) = - let leq_interval (al, au) (bl, bu) = al >=. bl && au <=. bu in - match xs, ys with - | [], _ -> true - | _, [] -> false - | (xl,xr)::xs', (yl,yr)::ys' -> - if leq_interval (xl,xr) (yl,yr) then - leq xs' ys - else if xr <. yl then - false - else - leq xs ys' - - let join ik (x: t) (y: t): t = - two_interval_sets_to_events x y |> - combined_event_list `Join |> - events_to_intervals |> - remove_empty_gaps - - let meet ik (x: t) (y: t): t = - two_interval_sets_to_events x y |> - combined_event_list `Meet |> - events_to_intervals - - let to_int = function - | [x] -> IArith.to_int x - | _ -> None - - let zero = [IArith.zero] - let one = [IArith.one] - let top_bool = [IArith.top_bool] - - let not_bool (x:t) = - let is_false x = equal x zero in - let is_true x = equal x one in - if is_true x then zero else if is_false x then one else top_bool - - let to_bool = function - | [(l,u)] when l =. Ints_t.zero && u =. Ints_t.zero -> Some false - | x -> if leq zero x then None else Some true - - let of_bool _ = function true -> one | false -> zero - - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm_interval ~suppress_ovwarn ~cast:false ik (x,y) - - let of_int ik (x: int_t) = of_interval ik (x, x) - - let lt ik x y = - match x, y with - | [], [] -> bot_of ik - | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> - let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in - let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in - if max_x <. min_y then - of_bool ik true - else if min_x >=. max_y then - of_bool ik false - else - top_bool - - let le ik x y = - match x, y with - | [], [] -> bot_of ik - | [], _ | _, [] -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | _, _ -> - let (max_x, min_y) = (maximal x |> Option.get, minimal y |> Option.get) in - let (min_x, max_y) = (minimal x |> Option.get, maximal y |> Option.get) in - if max_x <=. min_y then - of_bool ik true - else if min_x >. max_y then - of_bool ik false - else - top_bool - - let gt ik x y = not_bool @@ le ik x y - - let ge ik x y = not_bool @@ lt ik x y - - let eq ik x y = match x, y with - | (a, b)::[], (c, d)::[] when a =. b && c =. d && a =. c -> - one - | _ -> - if is_bot (meet ik x y) then - zero - else - top_bool - - let ne ik x y = not_bool @@ eq ik x y - let interval_to_int i = Interval.to_int (Some i) - let interval_to_bool i = Interval.to_bool (Some i) - - let log f ik (i1, i2) = - match (interval_to_bool i1, interval_to_bool i2) with - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - - let bit f ik (i1, i2) = - match (interval_to_int i1), (interval_to_int i2) with - | Some x, Some y -> (try of_int ik (f x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - - let bitcomp f ik (i1, i2) = - match (interval_to_int i1, interval_to_int i2) with - | Some x, Some y -> (try of_int ik (f x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{overflow=false; underflow=false})) - | _, _ -> (top_of ik,{overflow=false; underflow=false}) - - let logand ik x y = - let interval_logand = bit Ints_t.logand ik in - binop x y interval_logand - - let logor ik x y = - let interval_logor = bit Ints_t.logor ik in - binop x y interval_logor - - let logxor ik x y = - let interval_logxor = bit Ints_t.logxor ik in - binop x y interval_logxor - - let lognot ik x = - let interval_lognot i = - match interval_to_int i with - | Some x -> of_int ik (Ints_t.lognot x) |> fst - | _ -> top_of ik - in - unop x interval_lognot - - let shift_left ik x y = - let interval_shiftleft = bitcomp (fun x y -> Ints_t.shift_left x (Ints_t.to_int y)) ik in - binary_op_with_ovc x y interval_shiftleft - - let shift_right ik x y = - let interval_shiftright = bitcomp (fun x y -> Ints_t.shift_right x (Ints_t.to_int y)) ik in - binary_op_with_ovc x y interval_shiftright - - let c_lognot ik x = - let log1 f ik i1 = - match interval_to_bool i1 with - | Some x -> of_bool ik (f x) - | _ -> top_of ik - in - let interval_lognot = log1 not ik in - unop x interval_lognot - - let c_logand ik x y = - let interval_logand = log (&&) ik in - binop x y interval_logand - - let c_logor ik x y = - let interval_logor = log (||) ik in - binop x y interval_logor - - let add ?no_ov = binary_op_with_norm IArith.add - let sub ?no_ov = binary_op_with_norm IArith.sub - let mul ?no_ov = binary_op_with_norm IArith.mul - let neg ?no_ov = unary_op_with_norm IArith.neg - - let div ?no_ov ik x y = - let rec interval_div x (y1, y2) = begin - let top_of ik = top_of ik |> List.hd in - let is_zero v = v =. Ints_t.zero in - match y1, y2 with - | l, u when is_zero l && is_zero u -> top_of ik (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> interval_div x (Ints_t.one,y2) - | _, u when is_zero u -> interval_div x (y1, Ints_t.(neg one)) - | _ when leq (of_int ik (Ints_t.zero) |> fst) ([(y1,y2)]) -> top_of ik - | _ -> IArith.div x (y1, y2) - end - in binary_op_with_norm interval_div ik x y - - let rem ik x y = - let interval_rem (x, y) = - if Interval.is_top_of ik (Some x) && Interval.is_top_of ik (Some y) then - top_of ik - else - let (xl, xu) = x in let (yl, yu) = y in - let pos x = if x <. Ints_t.zero then Ints_t.neg x else x in - let b = (Ints_t.max (pos yl) (pos yu)) -. Ints_t.one in - let range = if xl >=. Ints_t.zero then (Ints_t.zero, Ints_t.min xu b) else (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit Ints_t.rem ik (x, y)) [range] - in - binop x y interval_rem - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov ik x = norm_intvs ~cast:true ik x - - (* - narrows down the extremeties of xs if they are equal to boundary values of the ikind with (possibly) narrower values from ys - *) - let narrow ik xs ys = match xs ,ys with - | [], _ -> [] | _ ,[] -> xs - | _, _ -> - let min_xs = minimal xs |> Option.get in - let max_xs = maximal xs |> Option.get in - let min_ys = minimal ys |> Option.get in - let max_ys = maximal ys |> Option.get in - let min_range,max_range = range ik in - let threshold = get_interval_threshold_widening () in - let min = if min_xs =. min_range || threshold && min_ys >. min_xs && IArith.is_lower_threshold min_xs then min_ys else min_xs in - let max = if max_xs =. max_range || threshold && max_ys <. max_xs && IArith.is_upper_threshold max_xs then max_ys else max_xs in - xs - |> (function (_, y)::z -> (min, y)::z | _ -> []) - |> List.rev - |> (function (x, _)::z -> (x, max)::z | _ -> []) - |> List.rev - - (* - 1. partitions the intervals of xs by assigning each of them to the an interval in ys that includes it. - and joins all intervals in xs assigned to the same interval in ys as one interval. - 2. checks for every pair of adjacent pairs whether the pairs did approach (if you compare the intervals from xs and ys) and merges them if it is the case. - 3. checks whether partitions at the extremeties are approaching infinity (and expands them to infinity. in that case) - - The expansion (between a pair of adjacent partitions or at extremeties ) stops at a threshold. - *) - let widen ik xs ys = - let (min_ik,max_ik) = range ik in - let threshold = get_bool "ana.int.interval_threshold_widening" in - let upper_threshold (_,u) = IArith.upper_threshold u max_ik in - let lower_threshold (l,_) = IArith.lower_threshold l min_ik in - (*obtain partitioning of xs intervals according to the ys interval that includes them*) - let rec interval_sets_to_partitions (ik: ikind) (acc : (int_t * int_t) option) (xs: t) (ys: t)= - match xs,ys with - | _, [] -> [] - | [], (y::ys) -> (acc,y):: interval_sets_to_partitions ik None [] ys - | (x::xs), (y::ys) when Interval.leq (Some x) (Some y) -> interval_sets_to_partitions ik (Interval.join ik acc (Some x)) xs (y::ys) - | (x::xs), (y::ys) -> (acc,y) :: interval_sets_to_partitions ik None (x::xs) ys - in - let interval_sets_to_partitions ik xs ys = interval_sets_to_partitions ik None xs ys in - (*merge a pair of adjacent partitions*) - let merge_pair ik (a,b) (c,d) = - let new_a = function - | None -> Some (upper_threshold b, upper_threshold b) - | Some (ax,ay) -> Some (ax, upper_threshold b) - in - let new_c = function - | None -> Some (lower_threshold d, lower_threshold d) - | Some (cx,cy) -> Some (lower_threshold d, cy) - in - if threshold && (lower_threshold d +. Ints_t.one) >. (upper_threshold b) then - [(new_a a,(fst b, upper_threshold b)); (new_c c, (lower_threshold d, snd d))] - else - [(Interval.join ik a c, (Interval.join ik (Some b) (Some d) |> Option.get))] - in - let partitions_are_approaching part_left part_right = match part_left, part_right with - | (Some (_, left_x), (_, left_y)), (Some (right_x, _), (right_y, _)) -> (right_x -. left_x) >. (right_y -. left_y) - | _,_ -> false - in - (*merge all approaching pairs of adjacent partitions*) - let rec merge_list ik = function - | [] -> [] - | x::y::xs when partitions_are_approaching x y -> merge_list ik ((merge_pair ik x y) @ xs) - | x::xs -> x :: merge_list ik xs - in - (*expands left extremity*) - let widen_left = function - | [] -> [] - | (None,(lb,rb))::ts -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (None, (lt,rb))::ts - | (Some (la,ra), (lb,rb))::ts when lb <. la -> let lt = if threshold then lower_threshold (lb,lb) else min_ik in (Some (la,ra),(lt,rb))::ts - | x -> x - in - (*expands right extremity*) - let widen_right x = - let map_rightmost = function - | [] -> [] - | (None,(lb,rb))::ts -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (None, (lb,ut))::ts - | (Some (la,ra), (lb,rb))::ts when ra <. rb -> let ut = if threshold then upper_threshold (rb,rb) else max_ik in (Some (la,ra),(lb,ut))::ts - | x -> x - in - List.rev x |> map_rightmost |> List.rev - in - interval_sets_to_partitions ik xs ys |> merge_list ik |> widen_left |> widen_right |> List.map snd - - let starting ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = norm_interval ik ~suppress_ovwarn (fst (range ik), n) - - let invariant_ikind e ik xs = - List.map (fun x -> Interval.invariant_ikind e ik (Some x)) xs |> - let open Invariant in List.fold_left (||) (bot ()) - - let modulo n k = - let result = Ints_t.rem n k in - if result >=. Ints_t.zero then result - else result +. k - - let refine_with_congruence ik (intvs: t) (cong: (int_t * int_t ) option): t = - let refine_with_congruence_interval ik (cong : (int_t * int_t ) option) (intv : (int_t * int_t ) option): t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if m =. Ints_t.zero && (c <. x || c >. y) then [] - else if m =. Ints_t.zero then - [(c, c)] - else - let (min_ik, max_ik) = range ik in - let rcx = - if x =. min_ik then x else - x +. (modulo (c -. x) (Ints_t.abs m)) in - let lcy = - if y =. max_ik then y else - y -. (modulo (y -. c) (Ints_t.abs m)) in - if rcx >. lcy then [] - else if rcx =. lcy then norm_interval ik (rcx, rcx) |> fst - else norm_interval ik (rcx, lcy) |> fst - | _ -> [] - in - List.concat_map (fun x -> refine_with_congruence_interval ik cong (Some x)) intvs - - let refine_with_interval ik xs = function None -> [] | Some (a,b) -> meet ik xs [(a,b)] - - let refine_with_incl_list ik intvs = function - | None -> intvs - | Some xs -> meet ik intvs (List.map (fun x -> (x,x)) xs) - - let excl_range_to_intervalset (ik: ikind) ((min, max): int_t * int_t) (excl: int_t): t = - let intv1 = (min, excl -. Ints_t.one) in - let intv2 = (excl +. Ints_t.one, max) in - norm_intvs ik ~suppress_ovwarn:true [intv1 ; intv2] |> fst - - let of_excl_list ik (excls: int_t list) = - let excl_list = List.map (excl_range_to_intervalset ik (range ik)) excls in - let res = List.fold_left (meet ik) (top_of ik) excl_list in - res - - let refine_with_excl_list ik (intv : t) = function - | None -> intv - | Some (xs, range) -> - let excl_to_intervalset (ik: ikind) ((rl, rh): (int64 * int64)) (excl: int_t): t = - excl_range_to_intervalset ik (Ints_t.of_bigint (Size.min_from_bit_range rl),Ints_t.of_bigint (Size.max_from_bit_range rh)) excl - in - let excl_list = List.map (excl_to_intervalset ik range) xs in - List.fold_left (meet ik) intv excl_list - - let project ik p t = t - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let list_pair_arb = QCheck.small_list pair_arb in - let canonize_randomly_generated_list = (fun x -> norm_intvs ik x |> fst) in - let shrink xs = GobQCheck.shrink list_pair_arb xs >|= canonize_randomly_generated_list - in QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) canonize_randomly_generated_list list_pair_arb) -end - module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t = struct include D From 3b5b9dfdd555f80b2108a15e44c084bb07366023 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:10:16 +0300 Subject: [PATCH 473/566] Rename IntDomain0 -> IntervalDomain for split --- .../value/cdomains/{intDomain0.ml => int/intervalDomain.ml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/cdomain/value/cdomains/{intDomain0.ml => int/intervalDomain.ml} (100%) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/int/intervalDomain.ml similarity index 100% rename from src/cdomain/value/cdomains/intDomain0.ml rename to src/cdomain/value/cdomains/int/intervalDomain.ml From 58725d34b132054a073290f07e6dd59b3049e849 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:10:43 +0300 Subject: [PATCH 474/566] Remove non-IntervalDomain parts --- .../value/cdomains/int/intervalDomain.ml | 983 +----------------- 1 file changed, 1 insertion(+), 982 deletions(-) diff --git a/src/cdomain/value/cdomains/int/intervalDomain.ml b/src/cdomain/value/cdomains/int/intervalDomain.ml index 67d7da2125..eff6bfff3e 100644 --- a/src/cdomain/value/cdomains/int/intervalDomain.ml +++ b/src/cdomain/value/cdomains/int/intervalDomain.ml @@ -1,639 +1,5 @@ -open GobConfig -open GoblintCil -open Pretty -open PrecisionUtil +open IntDomain0 -module M = Messages - -let (%) = Batteries.(%) -let (|?) = Batteries.(|?) - -exception IncompatibleIKinds of string -exception Unknown -exception Error -exception ArithmeticOnIntegerBot of string - - - - -(** Define records that hold mutable variables representing different Configuration values. - * These values are used to keep track of whether or not the corresponding Config values are en-/disabled *) -type ana_int_config_values = { - mutable interval_threshold_widening : bool option; - mutable interval_narrow_by_meet : bool option; - mutable def_exc_widen_by_join : bool option; - mutable interval_threshold_widening_constants : string option; - mutable refinement : string option; -} - -let ana_int_config: ana_int_config_values = { - interval_threshold_widening = None; - interval_narrow_by_meet = None; - def_exc_widen_by_join = None; - interval_threshold_widening_constants = None; - refinement = None; -} - -let get_interval_threshold_widening () = - if ana_int_config.interval_threshold_widening = None then - ana_int_config.interval_threshold_widening <- Some (get_bool "ana.int.interval_threshold_widening"); - Option.get ana_int_config.interval_threshold_widening - -let get_interval_narrow_by_meet () = - if ana_int_config.interval_narrow_by_meet = None then - ana_int_config.interval_narrow_by_meet <- Some (get_bool "ana.int.interval_narrow_by_meet"); - Option.get ana_int_config.interval_narrow_by_meet - -let get_def_exc_widen_by_join () = - if ana_int_config.def_exc_widen_by_join = None then - ana_int_config.def_exc_widen_by_join <- Some (get_bool "ana.int.def_exc_widen_by_join"); - Option.get ana_int_config.def_exc_widen_by_join - -let get_interval_threshold_widening_constants () = - if ana_int_config.interval_threshold_widening_constants = None then - ana_int_config.interval_threshold_widening_constants <- Some (get_string "ana.int.interval_threshold_widening_constants"); - Option.get ana_int_config.interval_threshold_widening_constants - -let get_refinement () = - if ana_int_config.refinement = None then - ana_int_config.refinement <- Some (get_string "ana.int.refinement"); - Option.get ana_int_config.refinement - - - -(** Whether for a given ikind, we should compute with wrap-around arithmetic. - * Always for unsigned types, for signed types if 'sem.int.signed_overflow' is 'assume_wraparound' *) -let should_wrap ik = not (Cil.isSigned ik) || get_string "sem.int.signed_overflow" = "assume_wraparound" - -(** Whether for a given ikind, we should assume there are no overflows. - * Always false for unsigned types, true for signed types if 'sem.int.signed_overflow' is 'assume_none' *) -let should_ignore_overflow ik = Cil.isSigned ik && get_string "sem.int.signed_overflow" = "assume_none" - -let widening_thresholds = ResettableLazy.from_fun WideningThresholds.thresholds -let widening_thresholds_desc = ResettableLazy.from_fun (List.rev % WideningThresholds.thresholds) - -type overflow_info = { overflow: bool; underflow: bool;} - -let set_overflow_flag ~cast ~underflow ~overflow ik = - if !AnalysisState.executing_speculative_computations then - (* Do not produce warnings when the operations are not actually happening in code *) - () - else - let signed = Cil.isSigned ik in - if !AnalysisState.postsolving && signed && not cast then - AnalysisState.svcomp_may_overflow := true; - let sign = if signed then "Signed" else "Unsigned" in - match underflow, overflow with - | true, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190; CWE 191] "%s integer overflow and underflow" sign - | true, false -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 191] "%s integer underflow" sign - | false, true -> - M.warn ~category:M.Category.Integer.overflow ~tags:[CWE 190] "%s integer overflow" sign - | false, false -> assert false - -let reset_lazy () = - ResettableLazy.reset widening_thresholds; - ResettableLazy.reset widening_thresholds_desc; - ana_int_config.interval_threshold_widening <- None; - ana_int_config.interval_narrow_by_meet <- None; - ana_int_config.def_exc_widen_by_join <- None; - ana_int_config.interval_threshold_widening_constants <- None; - ana_int_config.refinement <- None - -module type Arith = -sig - type t - val neg: t -> t - val add: t -> t -> t - val sub: t -> t -> t - val mul: t -> t -> t - val div: t -> t -> t - val rem: t -> t -> t - - val lt: t -> t -> t - val gt: t -> t -> t - val le: t -> t -> t - val ge: t -> t -> t - val eq: t -> t -> t - val ne: t -> t -> t - - val lognot: t -> t - val logand: t -> t -> t - val logor : t -> t -> t - val logxor: t -> t -> t - - val shift_left : t -> t -> t - val shift_right: t -> t -> t - - val c_lognot: t -> t - val c_logand: t -> t -> t - val c_logor : t -> t -> t - -end - -module type ArithIkind = -sig - type t - val neg: Cil.ikind -> t -> t - val add: Cil.ikind -> t -> t -> t - val sub: Cil.ikind -> t -> t -> t - val mul: Cil.ikind -> t -> t -> t - val div: Cil.ikind -> t -> t -> t - val rem: Cil.ikind -> t -> t -> t - - val lt: Cil.ikind -> t -> t -> t - val gt: Cil.ikind -> t -> t -> t - val le: Cil.ikind -> t -> t -> t - val ge: Cil.ikind -> t -> t -> t - val eq: Cil.ikind -> t -> t -> t - val ne: Cil.ikind -> t -> t -> t - - val lognot: Cil.ikind -> t -> t - val logand: Cil.ikind -> t -> t -> t - val logor : Cil.ikind -> t -> t -> t - val logxor: Cil.ikind -> t -> t -> t - - val shift_left : Cil.ikind -> t -> t -> t - val shift_right: Cil.ikind -> t -> t -> t - - val c_lognot: Cil.ikind -> t -> t - val c_logand: Cil.ikind -> t -> t -> t - val c_logor : Cil.ikind -> t -> t -> t - -end - -(* Shared functions between S and Z *) -module type B = -sig - include Lattice.S - type int_t - val bot_of: Cil.ikind -> t - val top_of: Cil.ikind -> t - val to_int: t -> int_t option - val equal_to: int_t -> t -> [`Eq | `Neq | `Top] - - val to_bool: t -> bool option - val to_excl_list: t -> (int_t list * (int64 * int64)) option - val of_excl_list: Cil.ikind -> int_t list -> t - val is_excl_list: t -> bool - - val to_incl_list: t -> int_t list option - - val maximal : t -> int_t option - val minimal : t -> int_t option - - val cast_to: ?suppress_ovwarn:bool -> ?torg:Cil.typ -> Cil.ikind -> t -> t -end - -(** Interface of IntDomain implementations that do not take ikinds for arithmetic operations yet. TODO: Should be ported to S in the future. *) -module type IkindUnawareS = -sig - include B - include Arith with type t := t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: int_t -> t - val of_bool: bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val arbitrary: unit -> t QCheck.arbitrary - val invariant: Cil.exp -> t -> Invariant.t -end - -(** Interface of IntDomain implementations taking an ikind for arithmetic operations *) -module type S = -sig - include B - include ArithIkind with type t:= t - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t - val neg : ?no_ov:bool -> Cil.ikind -> t -> t - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t - - val join: Cil.ikind -> t -> t -> t - val meet: Cil.ikind -> t -> t -> t - val narrow: Cil.ikind -> t -> t -> t - val widen: Cil.ikind -> t -> t -> t - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - val is_top_of: Cil.ikind -> t -> bool - val invariant_ikind : Cil.exp -> Cil.ikind -> t -> Invariant.t - - val refine_with_congruence: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_interval: Cil.ikind -> t -> (int_t * int_t) option -> t - val refine_with_excl_list: Cil.ikind -> t -> (int_t list * (int64 * int64)) option -> t - val refine_with_incl_list: Cil.ikind -> t -> int_t list option -> t - - val project: Cil.ikind -> int_precision -> t -> t - val arbitrary: Cil.ikind -> t QCheck.arbitrary -end - -module type SOverflow = -sig - - include S - - val add : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val sub : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val mul : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val div : ?no_ov:bool -> Cil.ikind -> t -> t -> t * overflow_info - - val neg : ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val cast_to : ?suppress_ovwarn:bool -> ?torg:Cil.typ -> ?no_ov:bool -> Cil.ikind -> t -> t * overflow_info - - val of_int : Cil.ikind -> int_t -> t * overflow_info - - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t * overflow_info - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t * overflow_info - - val shift_left : Cil.ikind -> t -> t -> t * overflow_info - - val shift_right : Cil.ikind -> t -> t -> t * overflow_info -end - -module type Y = -sig - (* include B *) - include B - include Arith with type t:= t - val of_int: Cil.ikind -> int_t -> t - val of_bool: Cil.ikind -> bool -> t - val of_interval: ?suppress_ovwarn:bool -> Cil.ikind -> int_t * int_t -> t - val of_congruence: Cil.ikind -> int_t * int_t -> t - - val starting : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val ending : ?suppress_ovwarn:bool -> Cil.ikind -> int_t -> t - val is_top_of: Cil.ikind -> t -> bool - - val project: int_precision -> t -> t - val invariant: Cil.exp -> t -> Invariant.t -end - -module type Z = Y with type int_t = Z.t - - -module IntDomLifter (I : S) = -struct - open Cil - 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 (GobPretty.sprintf "ikinds %a and %a are incompatible. Values: %a and %a" CilType.Ikind.pretty x.ikind CilType.Ikind.pretty y.ikind I.pretty x.v I.pretty y.v)) - let lift op x = {x with v = op x.ikind x.v } - (* For logical operations the result is of type int *) - let lift_logical op x = {v = op x.ikind x.v; ikind = Cil.IInt} - let lift2 op x y = check_ikinds x y; {x with v = op x.ikind x.v y.v } - let lift2_cmp op x y = check_ikinds x y; {v = op x.ikind x.v y.v; ikind = Cil.IInt} - - let bot_of ikind = { v = I.bot_of ikind; ikind} - let bot () = failwith "bot () is not implemented for IntDomLifter." - let is_bot x = I.is_bot x.v - let top_of ikind = { v = I.top_of ikind; ikind} - let top () = failwith "top () is not implemented for IntDomLifter." - let is_top x = I.is_top x.v - - (* Leq does not check for ikind, because it is used in invariant with arguments of different type. - TODO: check ikinds here and fix invariant to work with right ikinds *) - let leq x y = I.leq x.v y.v - let join = lift2 I.join - let meet = lift2 I.meet - let widen = lift2 I.widen - let narrow = lift2 I.narrow - - let show x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - "⊤" - else - I.show x.v (* TODO add ikind to output *) - let pretty () x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - Pretty.text "⊤" - else - I.pretty () x.v (* TODO add ikind to output *) - let pretty_diff () (x, y) = I.pretty_diff () (x.v, y.v) (* TODO check ikinds, add them to output *) - let printXml o x = - if not (GobConfig.get_bool "dbg.full-output") && I.is_top_of x.ikind x.v then - BatPrintf.fprintf o "\n\n⊤\n\n\n" - else - I.printXml o x.v (* TODO add ikind to output *) - (* This is for debugging *) - let name () = "IntDomLifter(" ^ (I.name ()) ^ ")" - let to_yojson x = I.to_yojson x.v - let invariant e x = - let e' = Cilfacade.mkCast ~e ~newt:(TInt (x.ikind, [])) in - I.invariant_ikind e' x.ikind x.v - let tag x = I.tag x.v - let arbitrary ik = failwith @@ "Arbitrary not implement for " ^ (name ()) ^ "." - let to_int x = I.to_int x.v - let of_int ikind x = { v = I.of_int ikind x; ikind} - let equal_to i x = I.equal_to i x.v - let to_bool x = I.to_bool x.v - let of_bool ikind b = { v = I.of_bool ikind b; ikind} - let to_excl_list x = I.to_excl_list x.v - let of_excl_list ikind is = {v = I.of_excl_list ikind is; ikind} - let is_excl_list x = I.is_excl_list x.v - let to_incl_list x = I.to_incl_list x.v - let of_interval ?(suppress_ovwarn=false) ikind (lb,ub) = {v = I.of_interval ~suppress_ovwarn ikind (lb,ub); ikind} - let of_congruence ikind (c,m) = {v = I.of_congruence ikind (c,m); ikind} - let starting ?(suppress_ovwarn=false) ikind i = {v = I.starting ~suppress_ovwarn ikind i; ikind} - let ending ?(suppress_ovwarn=false) ikind i = {v = I.ending ~suppress_ovwarn ikind i; ikind} - let maximal x = I.maximal x.v - let minimal x = I.minimal x.v - - let neg = lift I.neg - let add = lift2 I.add - let sub = lift2 I.sub - let mul = lift2 I.mul - let div = lift2 I.div - let rem = lift2 I.rem - let lt = lift2_cmp I.lt - let gt = lift2_cmp I.gt - let le = lift2_cmp I.le - let ge = lift2_cmp I.ge - let eq = lift2_cmp I.eq - let ne = lift2_cmp I.ne - let lognot = lift I.lognot - let logand = lift2 I.logand - let logor = lift2 I.logor - let logxor = lift2 I.logxor - let shift_left x y = {x with v = I.shift_left x.ikind x.v y.v } (* TODO check ikinds*) - let shift_right x y = {x with v = I.shift_right x.ikind x.v y.v } (* TODO check ikinds*) - let c_lognot = lift_logical I.c_lognot - let c_logand = lift2 I.c_logand - let c_logor = lift2 I.c_logor - - let cast_to ?(suppress_ovwarn=false) ?torg ikind x = {v = I.cast_to ~suppress_ovwarn ~torg:(TInt(x.ikind,[])) ikind x.v; ikind} - - let is_top_of ik x = ik = x.ikind && I.is_top_of ik x.v - - let relift x = { v = I.relift x.v; ikind = x.ikind } - - let project p v = { v = I.project v.ikind p v.v; ikind = v.ikind } -end - -module type Ikind = -sig - val ikind: unit -> Cil.ikind -end - -module PtrDiffIkind : Ikind = -struct - let ikind = Cilfacade.ptrdiff_ikind -end - -module IntDomWithDefaultIkind (I: Y) (Ik: Ikind) : Y with type t = I.t and type int_t = I.int_t = -struct - include I - let top () = I.top_of (Ik.ikind ()) - let bot () = I.bot_of (Ik.ikind ()) -end - -module Size = struct (* size in bits as int, range as int64 *) - open Cil - let sign x = if Z.compare x Z.zero < 0 then `Signed else `Unsigned - - let top_typ = TInt (ILongLong, []) - let min_for x = intKindForValue x (sign x = `Unsigned) - let bit = function (* bits needed for representation *) - | IBool -> 1 - | ik -> bytesSizeOfInt ik * 8 - let is_int64_big_int x = Z.fits_int64 x - let card ik = (* cardinality *) - let b = bit ik in - Z.shift_left Z.one b - let bits ik = (* highest bits for neg/pos values *) - let s = bit ik in - if isSigned ik then s-1, s-1 else 0, s - let bits_i64 ik = BatTuple.Tuple2.mapn Int64.of_int (bits ik) - let range ik = - let a,b = bits ik in - let x = if isSigned ik then Z.neg (Z.shift_left Z.one a) (* -2^a *) else Z.zero in - let y = Z.pred (Z.shift_left Z.one b) in (* 2^b - 1 *) - x,y - - let is_cast_injective ~from_type ~to_type = - let (from_min, from_max) = range (Cilfacade.get_ikind from_type) in - let (to_min, to_max) = range (Cilfacade.get_ikind to_type) in - if M.tracing then M.trace "int" "is_cast_injective %a (%a, %a) -> %a (%a, %a)" CilType.Typ.pretty from_type GobZ.pretty from_min GobZ.pretty from_max CilType.Typ.pretty to_type GobZ.pretty to_min GobZ.pretty to_max; - Z.compare to_min from_min <= 0 && Z.compare from_max to_max <= 0 - - let cast t x = (* TODO: overflow is implementation-dependent! *) - if t = IBool then - (* C11 6.3.1.2 Boolean type *) - if Z.equal x Z.zero then Z.zero else Z.one - else - let a,b = range t in - let c = card t in - let y = Z.erem x c in - let y = if Z.gt y b then Z.sub y c - else if Z.lt y a then Z.add y c - else y - in - if M.tracing then M.tracel "cast" "Cast %a to range [%a, %a] (%a) = %a (%s in int64)" GobZ.pretty x GobZ.pretty a GobZ.pretty b GobZ.pretty c GobZ.pretty y (if is_int64_big_int y then "fits" else "does not fit"); - y - - let min_range_sign_agnostic x = - let size ik = - let a,b = bits_i64 ik in - Int64.neg a,b - in - if sign x = `Signed then - size (min_for x) - else - let a, b = size (min_for x) in - if b <= 64L then - let upper_bound_less = Int64.sub b 1L in - let max_one_less = Z.(pred @@ shift_left Z.one (Int64.to_int upper_bound_less)) in - if x <= max_one_less then - a, upper_bound_less - else - a,b - else - a, b - - (* From the number of bits used to represent a positive value, determines the maximal representable value *) - let max_from_bit_range pos_bits = Z.(pred @@ shift_left Z.one (to_int (Z.of_int64 pos_bits))) - - (* From the number of bits used to represent a non-positive value, determines the minimal representable value *) - let min_from_bit_range neg_bits = Z.(if neg_bits = 0L then Z.zero else neg @@ shift_left Z.one (to_int (neg (Z.of_int64 neg_bits)))) - -end - - -module StdTop (B: sig type t val top_of: Cil.ikind -> t end) = struct - open B - (* these should be overwritten for better precision if possible: *) - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ik x = top_of ik - let ending ?(suppress_ovwarn=false) ik x = top_of ik - let maximal x = None - let minimal x = None -end - -module Std (B: sig - type t - val name: unit -> string - val top_of: Cil.ikind -> t - val bot_of: Cil.ikind -> t - val show: t -> string - val equal: t -> t -> bool - end) = struct - include Printable.StdLeaf - let name = B.name (* overwrite the one from Printable.Std *) - open B - let is_top x = failwith "is_top not implemented for IntDomain.Std" - let is_bot x = B.equal x (bot_of Cil.IInt) (* Here we assume that the representation of bottom is independent of the ikind - This may be true for intdomain implementations, but not e.g. for IntDomLifter. *) - let is_top_of ik x = B.equal x (top_of ik) - - (* all output is based on B.show *) - include Printable.SimpleShow ( - struct - type nonrec t = t - let show = show - end - ) - let pretty_diff () (x,y) = dprintf "%s: %a instead of %a" (name ()) pretty x pretty y - - include StdTop (B) -end - -(* Textbook interval arithmetic, without any overflow handling etc. *) -module IntervalArith (Ints_t : IntOps.IntOps) = struct - let min4 a b c d = Ints_t.min (Ints_t.min a b) (Ints_t.min c d) - let max4 a b c d = Ints_t.max (Ints_t.max a b) (Ints_t.max c d) - - let mul (x1, x2) (y1, y2) = - let x1y1 = (Ints_t.mul x1 y1) in - let x1y2 = (Ints_t.mul x1 y2) in - let x2y1 = (Ints_t.mul x2 y1) in - let x2y2 = (Ints_t.mul x2 y2) in - (min4 x1y1 x1y2 x2y1 x2y2, max4 x1y1 x1y2 x2y1 x2y2) - - let shift_left (x1,x2) (y1,y2) = - let y1p = Ints_t.shift_left Ints_t.one y1 in - let y2p = Ints_t.shift_left Ints_t.one y2 in - mul (x1, x2) (y1p, y2p) - - let div (x1, x2) (y1, y2) = - let x1y1n = (Ints_t.div x1 y1) in - let x1y2n = (Ints_t.div x1 y2) in - let x2y1n = (Ints_t.div x2 y1) in - let x2y2n = (Ints_t.div x2 y2) in - let x1y1p = (Ints_t.div x1 y1) in - let x1y2p = (Ints_t.div x1 y2) in - let x2y1p = (Ints_t.div x2 y1) in - let x2y2p = (Ints_t.div x2 y2) in - (min4 x1y1n x1y2n x2y1n x2y2n, max4 x1y1p x1y2p x2y1p x2y2p) - - let add (x1, x2) (y1, y2) = (Ints_t.add x1 y1, Ints_t.add x2 y2) - let sub (x1, x2) (y1, y2) = (Ints_t.sub x1 y2, Ints_t.sub x2 y1) - - let neg (x1, x2) = (Ints_t.neg x2, Ints_t.neg x1) - - let one = (Ints_t.one, Ints_t.one) - let zero = (Ints_t.zero, Ints_t.zero) - let top_bool = (Ints_t.zero, Ints_t.one) - - let to_int (x1, x2) = - if Ints_t.equal x1 x2 then Some x1 else None - - let upper_threshold u max_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - let max_ik' = Ints_t.to_bigint max_ik in - let t = List.find_opt (fun x -> Z.compare u x <= 0 && Z.compare x max_ik' <= 0) ts in - BatOption.map_default Ints_t.of_bigint max_ik t - let lower_threshold l min_ik = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - let min_ik' = Ints_t.to_bigint min_ik in - let t = List.find_opt (fun x -> Z.compare l x >= 0 && Z.compare x min_ik' >= 0) ts in - BatOption.map_default Ints_t.of_bigint min_ik t - let is_upper_threshold u = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.upper_thresholds () else ResettableLazy.force widening_thresholds in - let u = Ints_t.to_bigint u in - List.exists (Z.equal u) ts - let is_lower_threshold l = - let ts = if get_interval_threshold_widening_constants () = "comparisons" then WideningThresholds.lower_thresholds () else ResettableLazy.force widening_thresholds_desc in - let l = Ints_t.to_bigint l in - List.exists (Z.equal l) ts -end - -module IntInvariant = -struct - let of_int e ik x = - if get_bool "witness.invariant.exact" then - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) - else - Invariant.none - - let of_incl_list e ik ps = - match ps with - | [_; _] when ik = IBool && not (get_bool "witness.invariant.inexact-type-bounds") -> - assert (List.mem Z.zero ps); - assert (List.mem Z.one ps); - Invariant.none - | [_] when get_bool "witness.invariant.exact" -> - Invariant.none - | _ :: _ :: _ - | [_] | [] -> - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in - Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) (Invariant.bot ()) ps - - let of_interval_opt e ik = function - | (Some x1, Some x2) when Z.equal x1 x2 -> - of_int e ik x1 - | x1_opt, x2_opt -> - let (min_ik, max_ik) = Size.range ik in - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in - let i1 = - match x1_opt, inexact_type_bounds with - | Some x1, false when Z.equal min_ik x1 -> Invariant.none - | Some x1, _ -> Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1, e, intType)) - | None, _ -> Invariant.none - in - let i2 = - match x2_opt, inexact_type_bounds with - | Some x2, false when Z.equal x2 max_ik -> Invariant.none - | Some x2, _ -> Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) - | None, _ -> Invariant.none - in - Invariant.(i1 && i2) - - let of_interval e ik (x1, x2) = - of_interval_opt e ik (Some x1, Some x2) - - let of_excl_list e ik ns = - List.fold_left (fun a x -> - let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in - Invariant.(a && i) - ) (Invariant.top ()) ns -end module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = struct @@ -1046,350 +412,3 @@ struct let project ik p t = t end - -module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t = struct - include D - - let add ?no_ov ik x y = fst @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = fst @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = fst @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = fst @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = fst @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = fst @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = fst @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = fst @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = fst @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = fst @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = fst @@ D.shift_left ik x y - - let shift_right ik x y = fst @@ D.shift_right ik x y -end - -module IntIkind = struct let ikind () = Cil.IInt end -module Interval = IntervalFunctor (IntOps.BigIntOps) -module Interval32 = IntDomWithDefaultIkind (IntDomLifter (SOverflowUnlifter (IntervalFunctor (IntOps.Int64Ops)))) (IntIkind) - -module Integers (Ints_t : IntOps.IntOps): IkindUnawareS with type t = Ints_t.t and type int_t = Ints_t.t = (* no top/bot, order is <= *) -struct - include Printable.Std - let name () = "integers" - type t = Ints_t.t [@@deriving eq, ord, hash] - type int_t = Ints_t.t - let top () = raise Unknown - let bot () = raise Error - let top_of ik = top () - let bot_of ik = bot () - let show (x: Ints_t.t) = Ints_t.to_string x - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - (* is_top and is_bot are never called, but if they were, the Std impl would raise their exception, so we overwrite them: *) - let is_top _ = false - let is_bot _ = false - - let equal_to i x = if i > x then `Neq else `Top - let leq x y = x <= y - let join x y = if Ints_t.compare x y > 0 then x else y - let widen = join - let meet x y = if Ints_t.compare x y > 0 then y else x - let narrow = meet - - let of_bool x = if x then Ints_t.one else Ints_t.zero - let to_bool' x = x <> Ints_t.zero - let to_bool x = Some (to_bool' x) - let of_int x = x - let to_int x = Some x - - let neg = Ints_t.neg - let add = Ints_t.add (* TODO: signed overflow is undefined behavior! *) - let sub = Ints_t.sub - let mul = Ints_t.mul - let div = Ints_t.div - let rem = Ints_t.rem - let lt n1 n2 = of_bool (n1 < n2) - let gt n1 n2 = of_bool (n1 > n2) - let le n1 n2 = of_bool (n1 <= n2) - let ge n1 n2 = of_bool (n1 >= n2) - let eq n1 n2 = of_bool (n1 = n2) - let ne n1 n2 = of_bool (n1 <> n2) - let lognot = Ints_t.lognot - let logand = Ints_t.logand - let logor = Ints_t.logor - let logxor = Ints_t.logxor - let shift_left n1 n2 = Ints_t.shift_left n1 (Ints_t.to_int n2) - let shift_right n1 n2 = Ints_t.shift_right n1 (Ints_t.to_int n2) - let c_lognot n1 = of_bool (not (to_bool' n1)) - let c_logand n1 n2 = of_bool ((to_bool' n1) && (to_bool' n2)) - let c_logor n1 n2 = of_bool ((to_bool' n1) || (to_bool' n2)) - let cast_to ?(suppress_ovwarn=false) ?torg t x = failwith @@ "Cast_to not implemented for " ^ (name ()) ^ "." - let arbitrary ik = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 (* TODO: use ikind *) - let invariant _ _ = Invariant.none (* TODO *) -end - -module FlatPureIntegers: IkindUnawareS with type t = int64 and type int_t = int64 = (* Integers, but raises Unknown/Error on join/meet *) -struct - include Integers(IntOps.Int64Ops) - let top () = raise Unknown - let bot () = raise Error - let leq = equal - let pretty_diff () (x,y) = Pretty.dprintf "Integer %a instead of %a" pretty x pretty y - let join x y = if equal x y then x else top () - let meet x y = if equal x y then x else bot () -end - -module Flat (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Lift, but goes to `Top/`Bot if Base raises Unknown/Error *) -struct - type int_t = Base.int_t - include Lattice.FlatConf (struct - include Printable.DefaultConf - let top_name = "Unknown int" - let bot_name = "Error int" - end) (Base) - - let top_of ik = top () - let bot_of ik = bot () - - - let name () = "flat integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let to_excl_list x = None - let of_excl_list ik x = top_of ik - let is_excl_list x = false - let to_incl_list x = None - let of_interval ?(suppress_ovwarn=false) ik x = top_of ik - let of_congruence ik x = top_of ik - let starting ?(suppress_ovwarn=false) ikind x = top_of ikind - let ending ?(suppress_ovwarn=false) ikind x = top_of ikind - let maximal x = None - let minimal x = None - - let lift1 f x = match x with - | `Lifted x -> - (try `Lifted (f x) with Unknown -> `Top | Error -> `Bot) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> - (try `Lifted (f x y) with Unknown -> `Top | Error -> `Bot) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Lift (Base: IkindUnawareS): IkindUnawareS with type t = [ `Bot | `Lifted of Base.t | `Top ] and type int_t = Base.int_t = (* identical to Flat, but does not go to `Top/Bot` if Base raises Unknown/Error *) -struct - include Lattice.LiftPO (struct - include Printable.DefaultConf - let top_name = "MaxInt" - let bot_name = "MinInt" - end) (Base) - type int_t = Base.int_t - let top_of ik = top () - let bot_of ik = bot () - include StdTop (struct type nonrec t = t let top_of = top_of end) - - let name () = "lifted integers" - let cast_to ?(suppress_ovwarn=false) ?torg t = function - | `Lifted x -> `Lifted (Base.cast_to t x) - | x -> x - - let equal_to i = function - | `Bot -> failwith "unsupported: equal_to with bottom" - | `Top -> `Top - | `Lifted x -> Base.equal_to i x - - let of_int x = `Lifted (Base.of_int x) - let to_int x = match x with - | `Lifted x -> Base.to_int x - | _ -> None - - let of_bool x = `Lifted (Base.of_bool x) - let to_bool x = match x with - | `Lifted x -> Base.to_bool x - | _ -> None - - let lift1 f x = match x with - | `Lifted x -> `Lifted (f x) - | x -> x - let lift2 f x y = match x,y with - | `Lifted x, `Lifted y -> `Lifted (f x y) - | `Bot, `Bot -> `Bot - | _ -> `Top - - let neg = lift1 Base.neg - let add = lift2 Base.add - let sub = lift2 Base.sub - let mul = lift2 Base.mul - let div = lift2 Base.div - let rem = lift2 Base.rem - let lt = lift2 Base.lt - let gt = lift2 Base.gt - let le = lift2 Base.le - let ge = lift2 Base.ge - let eq = lift2 Base.eq - let ne = lift2 Base.ne - let lognot = lift1 Base.lognot - let logand = lift2 Base.logand - let logor = lift2 Base.logor - let logxor = lift2 Base.logxor - let shift_left = lift2 Base.shift_left - let shift_right = lift2 Base.shift_right - let c_lognot = lift1 Base.c_lognot - let c_logand = lift2 Base.c_logand - let c_logor = lift2 Base.c_logor - - let invariant e = function - | `Lifted x -> Base.invariant e x - | `Top | `Bot -> Invariant.none -end - -module Flattened = Flat (Integers (IntOps.Int64Ops)) -module Lifted = Lift (Integers (IntOps.Int64Ops)) - -module Reverse (Base: IkindUnawareS) = -struct - include Base - include (Lattice.Reverse (Base) : Lattice.S with type t := Base.t) -end - -module BISet = struct - include SetDomain.Make (IntOps.BigIntOps) - let is_singleton s = cardinal s = 1 -end - -(* The module [Exclusion] constains common functionality about handling of exclusion sets between [DefExc] and [Enums] *) -module Exclusion = -struct - module R = Interval32 - (* We use these types for the functions in this module to make the intended meaning more explicit *) - type t = Exc of BISet.t * Interval32.t - type inc = Inc of BISet.t [@@unboxed] - let max_of_range r = Size.max_from_bit_range (Option.get (R.maximal r)) - let min_of_range r = Size.min_from_bit_range (Option.get (R.minimal r)) - let cardinality_of_range r = Z.succ (Z.add (Z.neg (min_of_range r)) (max_of_range r)) - - let cardinality_BISet s = - Z.of_int (BISet.cardinal s) - - let leq_excl_incl (Exc (xs, r)) (Inc ys) = - (* For a <= b to hold, the cardinalities must fit, i.e. |a| <= |b|, which implies |min_r, max_r| - |xs| <= |ys|. We check this first. *) - let lower_bound_cardinality_a = Z.sub (cardinality_of_range r) (cardinality_BISet xs) in - let card_b = cardinality_BISet ys in - if Z.compare lower_bound_cardinality_a card_b > 0 then - false - else (* The cardinality did fit, so we check for all elements that are represented by range r, whether they are in (xs union ys) *) - let min_a = min_of_range r in - let max_a = max_of_range r in - GobZ.for_all_range (fun el -> BISet.mem el xs || BISet.mem el ys) (min_a, max_a) - - let leq (Exc (xs, r)) (Exc (ys, s)) = - let min_a, max_a = min_of_range r, max_of_range r in - let excluded_check = BISet.for_all (fun y -> BISet.mem y xs || Z.compare y min_a < 0 || Z.compare y max_a > 0) ys in (* if true, then the values ys, that are not in b, also do not occur in a *) - if not excluded_check - then false - else begin (* Check whether all elements that are in the range r, but not in s, are in xs, i.e. excluded. *) - if R.leq r s then true - else begin if Z.compare (cardinality_BISet xs) (Z.sub (cardinality_of_range r) (cardinality_of_range s)) >= 0 (* Check whether the number of excluded elements in a is as least as big as |min_r, max_r| - |min_s, max_s| *) - then - let min_b, max_b = min_of_range s, max_of_range s in - let leq1 = (* check whether the elements in [r_l; s_l-1] are all in xs, i.e. excluded *) - if Z.compare min_a min_b < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (min_a, Z.pred min_b) - else - true - in - let leq2 () = (* check whether the elements in [s_u+1; r_u] are all in xs, i.e. excluded *) - if Z.compare max_b max_a < 0 then - GobZ.for_all_range (fun x -> BISet.mem x xs) (Z.succ max_b, max_a) - else - true - in - leq1 && (leq2 ()) - else - false - end - end -end - -module SOverflowLifter (D : S) : SOverflow with type int_t = D.int_t and type t = D.t = struct - - include D - - let lift v = (v, {overflow=false; underflow=false}) - - let add ?no_ov ik x y = lift @@ D.add ?no_ov ik x y - - let sub ?no_ov ik x y = lift @@ D.sub ?no_ov ik x y - - let mul ?no_ov ik x y = lift @@ D.mul ?no_ov ik x y - - let div ?no_ov ik x y = lift @@ D.div ?no_ov ik x y - - let neg ?no_ov ik x = lift @@ D.neg ?no_ov ik x - - let cast_to ?suppress_ovwarn ?torg ?no_ov ik x = lift @@ D.cast_to ?suppress_ovwarn ?torg ?no_ov ik x - - let of_int ik x = lift @@ D.of_int ik x - - let of_interval ?suppress_ovwarn ik x = lift @@ D.of_interval ?suppress_ovwarn ik x - - let starting ?suppress_ovwarn ik x = lift @@ D.starting ?suppress_ovwarn ik x - - let ending ?suppress_ovwarn ik x = lift @@ D.ending ?suppress_ovwarn ik x - - let shift_left ik x y = lift @@ D.shift_left ik x y - - let shift_right ik x y = lift @@ D.shift_right ik x y - -end From 127603f4cf2c71bbc833df3932ee69d24b6bb914 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:11:08 +0300 Subject: [PATCH 475/566] Remove IntervalFunctor from IntDomain0 --- src/cdomain/value/cdomains/intDomain0.ml | 412 ----------------------- 1 file changed, 412 deletions(-) diff --git a/src/cdomain/value/cdomains/intDomain0.ml b/src/cdomain/value/cdomains/intDomain0.ml index 67d7da2125..377ef1576d 100644 --- a/src/cdomain/value/cdomains/intDomain0.ml +++ b/src/cdomain/value/cdomains/intDomain0.ml @@ -635,418 +635,6 @@ struct ) (Invariant.top ()) ns end -module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = -struct - let name () = "intervals" - type int_t = Ints_t.t - type t = (Ints_t.t * Ints_t.t) option [@@deriving eq, ord, hash] - module IArith = IntervalArith (Ints_t) - - let range ik = BatTuple.Tuple2.mapn Ints_t.of_bigint (Size.range ik) - - let top () = failwith @@ "top () not implemented for " ^ (name ()) - let top_of ik = Some (range ik) - let bot () = None - let bot_of ik = bot () (* TODO: improve *) - - let show = function None -> "bottom" | Some (x,y) -> "["^Ints_t.to_string x^","^Ints_t.to_string y^"]" - - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - - let equal_to i = function - | None -> failwith "unsupported: equal_to with bottom" - | Some (a, b) -> - if a = b && b = i then `Eq else if Ints_t.compare a i <= 0 && Ints_t.compare i b <=0 then `Top else `Neq - - let norm ?(suppress_ovwarn=false) ?(cast=false) ik : (t -> t * overflow_info) = function None -> (None, {underflow=false; overflow=false}) | Some (x,y) -> - if Ints_t.compare x y > 0 then - (None,{underflow=false; overflow=false}) - else ( - let (min_ik, max_ik) = range ik in - let underflow = Ints_t.compare min_ik x > 0 in - let overflow = Ints_t.compare max_ik y < 0 in - let ov_info = { underflow = underflow && not suppress_ovwarn; overflow = overflow && not suppress_ovwarn } in - let v = - if underflow || overflow then - if should_wrap ik then (* could add [|| cast], but that's GCC implementation-defined behavior: https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation *) - (* We can only soundly wrap if at most one overflow occurred, otherwise the minimal and maximal values of the interval *) - (* on Z will not safely contain the minimal and maximal elements after the cast *) - let diff = Ints_t.abs (Ints_t.sub max_ik min_ik) in - let resdiff = Ints_t.abs (Ints_t.sub y x) in - if Ints_t.compare resdiff diff > 0 then - top_of ik - else - let l = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint x) in - let u = Ints_t.of_bigint @@ Size.cast ik (Ints_t.to_bigint y) in - if Ints_t.compare l u <= 0 then - Some (l, u) - else - (* Interval that wraps around (begins to the right of its end). We can not represent such intervals *) - top_of ik - else if not cast && should_ignore_overflow ik then - let tl, tu = BatOption.get @@ top_of ik in - Some (Ints_t.max tl x, Ints_t.min tu y) - else - top_of ik - else - Some (x,y) - in - (v, ov_info) - ) - - let leq (x:t) (y:t) = - match x, y with - | None, _ -> true - | Some _, None -> false - | Some (x1,x2), Some (y1,y2) -> Ints_t.compare x1 y1 >= 0 && Ints_t.compare x2 y2 <= 0 - - let join ik (x:t) y = - match x, y with - | None, z | z, None -> z - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.min x1 y1, Ints_t.max x2 y2) |> fst - - let meet ik (x:t) y = - match x, y with - | None, z | z, None -> None - | Some (x1,x2), Some (y1,y2) -> norm ik @@ Some (Ints_t.max x1 y1, Ints_t.min x2 y2) |> fst - - (* TODO: change to_int signature so it returns a big_int *) - let to_int x = Option.bind x (IArith.to_int) - let of_interval ?(suppress_ovwarn=false) ik (x,y) = norm ~suppress_ovwarn ik @@ Some (x,y) - let of_int ik (x: int_t) = of_interval ik (x,x) - let zero = Some IArith.zero - let one = Some IArith.one - let top_bool = Some IArith.top_bool - - let of_bool _ik = function true -> one | false -> zero - let to_bool (a: t) = match a with - | None -> None - | Some (l, u) when Ints_t.compare l Ints_t.zero = 0 && Ints_t.compare u Ints_t.zero = 0 -> Some false - | x -> if leq zero x then None else Some true - - let starting ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (n, snd (range ik)) - - let ending ?(suppress_ovwarn=false) ik n = - norm ~suppress_ovwarn ik @@ Some (fst (range ik), n) - - (* TODO: change signature of maximal, minimal to return big_int*) - let maximal = function None -> None | Some (x,y) -> Some y - let minimal = function None -> None | Some (x,y) -> Some x - - let cast_to ?(suppress_ovwarn=false) ?torg ?no_ov t = norm ~cast:true t (* norm does all overflow handling *) - - let widen ik x y = - match x, y with - | None, z | z, None -> z - | Some (l0,u0), Some (l1,u1) -> - let (min_ik, max_ik) = range ik in - let threshold = get_interval_threshold_widening () in - let l2 = - if Ints_t.compare l0 l1 = 0 then l0 - else if threshold then IArith.lower_threshold l1 min_ik - else min_ik - in - let u2 = - if Ints_t.compare u0 u1 = 0 then u0 - else if threshold then IArith.upper_threshold u1 max_ik - else max_ik - in - norm ik @@ Some (l2,u2) |> fst - let widen ik x y = - let r = widen ik x y in - if M.tracing && not (equal x y) then M.tracel "int" "interval widen %a %a -> %a" pretty x pretty y pretty r; - assert (leq x y); (* TODO: remove for performance reasons? *) - r - - let narrow ik x y = - match x, y with - | _,None | None, _ -> None - | Some (x1,x2), Some (y1,y2) -> - let threshold = get_interval_threshold_widening () in - let (min_ik, max_ik) = range ik in - let lr = if Ints_t.compare min_ik x1 = 0 || threshold && Ints_t.compare y1 x1 > 0 && IArith.is_lower_threshold x1 then y1 else x1 in - let ur = if Ints_t.compare max_ik x2 = 0 || threshold && Ints_t.compare y2 x2 < 0 && IArith.is_upper_threshold x2 then y2 else x2 in - norm ik @@ Some (lr,ur) |> fst - - - let narrow ik x y = - if get_interval_narrow_by_meet () then - meet ik x y - else - narrow ik x y - - let log f ~annihilator ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_bool i1, to_bool i2 with - | Some x, _ when x = annihilator -> of_bool ik annihilator - | _, Some y when y = annihilator -> of_bool ik annihilator - | Some x, Some y -> of_bool ik (f x y) - | _ -> top_of ik - - let c_logor = log (||) ~annihilator:true - let c_logand = log (&&) ~annihilator:false - - let log1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_bool i1 with - | Some x -> of_bool ik (f ik x) - | _ -> top_of ik - - let c_lognot = log1 (fun _ik -> not) - - let bit f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) |> fst with Division_by_zero -> top_of ik) - | _ -> top_of ik - - let bitcomp f ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (f ik x y) with Division_by_zero | Invalid_argument _ -> (top_of ik,{underflow=false; overflow=false})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let logxor = bit (fun _ik -> Ints_t.logxor) - - let logand ik i1 i2 = - match is_bot i1, is_bot i2 with - | true, true -> bot_of ik - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show i1) (show i2))) - | _ -> - match to_int i1, to_int i2 with - | Some x, Some y -> (try of_int ik (Ints_t.logand x y) |> fst with Division_by_zero -> top_of ik) - | _, Some y when Ints_t.equal y Ints_t.zero -> of_int ik Ints_t.zero |> fst - | _, Some y when Ints_t.equal y Ints_t.one -> of_interval ik (Ints_t.zero, Ints_t.one) |> fst - | _ -> top_of ik - - let logor = bit (fun _ik -> Ints_t.logor) - - let bit1 f ik i1 = - if is_bot i1 then - bot_of ik - else - match to_int i1 with - | Some x -> of_int ik (f ik x) |> fst - | _ -> top_of ik - - let lognot = bit1 (fun _ik -> Ints_t.lognot) - let shift_right = bitcomp (fun _ik x y -> Ints_t.shift_right x (Ints_t.to_int y)) - - let neg ?no_ov ik = function None -> (None,{underflow=false; overflow=false}) | Some x -> norm ik @@ Some (IArith.neg x) - - let binary_op_with_norm ?no_ov op ik x y = match x, y with - | None, None -> (None, {overflow=false; underflow= false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some x, Some y -> norm ik @@ Some (op x y) - - let add ?no_ov = binary_op_with_norm IArith.add - let mul ?no_ov = binary_op_with_norm IArith.mul - let sub ?no_ov = binary_op_with_norm IArith.sub - - let shift_left ik a b = - match is_bot a, is_bot b with - | true, true -> (bot_of ik,{underflow=false; overflow=false}) - | true, _ - | _ , true -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show a) (show b))) - | _ -> - match a, minimal b, maximal b with - | Some a, Some bl, Some bu when (Ints_t.compare bl Ints_t.zero >= 0) -> - (try - let r = IArith.shift_left a (Ints_t.to_int bl, Ints_t.to_int bu) in - norm ik @@ Some r - with Z.Overflow -> (top_of ik,{underflow=false; overflow=true})) - | _ -> (top_of ik,{underflow=true; overflow=true}) - - let rem ik x y = match x, y with - | None, None -> None - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (xl, xu), Some (yl, yu) -> - if is_top_of ik x && is_top_of ik y then - (* This is needed to preserve soundness also on things bigger than int32 e.g. *) - (* x: 3803957176L -> T in Interval32 *) - (* y: 4209861404L -> T in Interval32 *) - (* x % y: 3803957176L -> T in Interval32 *) - (* T in Interval32 is [-2147483648,2147483647] *) - (* the code below computes [-2147483647,2147483647] for this though which is unsound *) - top_of ik - else - (* If we have definite values, Ints_t.rem will give a definite result. - * Otherwise we meet with a [range] the result can be in. - * This range is [0, min xu b] if x is positive, and [max xl -b, min xu b] if x can be negative. - * The precise bound b is one smaller than the maximum bound. Negative y give the same result as positive. *) - let pos x = if Ints_t.compare x Ints_t.zero < 0 then Ints_t.neg x else x in - let b = Ints_t.sub (Ints_t.max (pos yl) (pos yu)) Ints_t.one in - let range = if Ints_t.compare xl Ints_t.zero>= 0 then Some (Ints_t.zero, Ints_t.min xu b) else Some (Ints_t.max xl (Ints_t.neg b), Ints_t.min (Ints_t.max (pos xl) (pos xu)) b) in - meet ik (bit (fun _ik -> Ints_t.rem) ik x y) range - - let rec div ?no_ov ik x y = - match x, y with - | None, None -> (bot (),{underflow=false; overflow=false}) - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | (Some (x1,x2) as x), (Some (y1,y2) as y) -> - begin - let is_zero v = Ints_t.compare v Ints_t.zero = 0 in - match y1, y2 with - | l, u when is_zero l && is_zero u -> (top_of ik,{underflow=false; overflow=false}) (* TODO warn about undefined behavior *) - | l, _ when is_zero l -> div ik (Some (x1,x2)) (Some (Ints_t.one,y2)) - | _, u when is_zero u -> div ik (Some (x1,x2)) (Some (y1, Ints_t.(neg one))) - | _ when leq (of_int ik (Ints_t.zero) |> fst) (Some (y1,y2)) -> (top_of ik,{underflow=false; overflow=false}) - | _ -> binary_op_with_norm IArith.div ik x y - end - - let ne ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik true - else if Ints_t.compare x2 y1 <= 0 && Ints_t.compare y2 x1 <= 0 then - of_bool ik false - else top_bool - - let eq ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 && Ints_t.compare x2 y1 <= 0 then - of_bool ik true - else if Ints_t.compare y2 x1 < 0 || Ints_t.compare x2 y1 < 0 then - of_bool ik false - else top_bool - - let ge ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 <= 0 then of_bool ik true - else if Ints_t.compare x2 y1 < 0 then of_bool ik false - else top_bool - - let le ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 <= 0 then of_bool ik true - else if Ints_t.compare y2 x1 < 0 then of_bool ik false - else top_bool - - let gt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare y2 x1 < 0 then of_bool ik true - else if Ints_t.compare x2 y1 <= 0 then of_bool ik false - else top_bool - - let lt ik x y = - match x, y with - | None, None -> bot_of ik - | None, _ | _, None -> raise (ArithmeticOnIntegerBot (Printf.sprintf "%s op %s" (show x) (show y))) - | Some (x1,x2), Some (y1,y2) -> - if Ints_t.compare x2 y1 < 0 then of_bool ik true - else if Ints_t.compare y2 x1 <= 0 then of_bool ik false - else top_bool - - let invariant_ikind e ik = function - | Some (x1, x2) -> - let (x1', x2') = BatTuple.Tuple2.mapn Ints_t.to_bigint (x1, x2) in - IntInvariant.of_interval e ik (x1', x2') - | None -> Invariant.none - - let arbitrary ik = - let open QCheck.Iter in - (* let int_arb = QCheck.map ~rev:Ints_t.to_bigint Ints_t.of_bigint GobQCheck.Arbitrary.big_int in *) - (* TODO: apparently bigints are really slow compared to int64 for domaintest *) - let int_arb = QCheck.map ~rev:Ints_t.to_int64 Ints_t.of_int64 GobQCheck.Arbitrary.int64 in - let pair_arb = QCheck.pair int_arb int_arb in - let shrink = function - | Some (l, u) -> (return None) <+> (GobQCheck.shrink pair_arb (l, u) >|= of_interval ik >|= fst) - | None -> empty - in - QCheck.(set_shrink shrink @@ set_print show @@ map (*~rev:BatOption.get*) (fun x -> of_interval ik x |> fst ) pair_arb) - - let modulo n k = - let result = Ints_t.rem n k in - if Ints_t.compare result Ints_t.zero >= 0 then result - else Ints_t.add result k - - let refine_with_congruence ik (intv : t) (cong : (int_t * int_t ) option) : t = - match intv, cong with - | Some (x, y), Some (c, m) -> - if Ints_t.equal m Ints_t.zero && (Ints_t.compare c x < 0 || Ints_t.compare c y > 0) then None - else if Ints_t.equal m Ints_t.zero then - Some (c, c) - else - let (min_ik, max_ik) = range ik in - let rcx = - if Ints_t.equal x min_ik then x else - Ints_t.add x (modulo (Ints_t.sub c x) (Ints_t.abs m)) in - let lcy = - if Ints_t.equal y max_ik then y else - Ints_t.sub y (modulo (Ints_t.sub y c) (Ints_t.abs m)) in - if Ints_t.compare rcx lcy > 0 then None - else if Ints_t.equal rcx lcy then norm ik @@ Some (rcx, rcx) |> fst - else norm ik @@ Some (rcx, lcy) |> fst - | _ -> None - - let refine_with_congruence ik x y = - let refn = refine_with_congruence ik x y in - if M.tracing then M.trace "refine" "int_refine_with_congruence %a %a -> %a" pretty x pretty y pretty refn; - refn - - let refine_with_interval ik a b = meet ik a b - - let refine_with_excl_list ik (intv : t) (excl : (int_t list * (int64 * int64)) option) : t = - match intv, excl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls, (rl, rh)) -> - let rec shrink op b = - let new_b = (op b (Ints_t.of_int(Bool.to_int(BatList.mem_cmp Ints_t.compare b ls)))) in - if not (Ints_t.equal b new_b) then shrink op new_b else new_b - in - let (min_ik, max_ik) = range ik in - let l' = if Ints_t.equal l min_ik then l else shrink Ints_t.add l in - let u' = if Ints_t.equal u max_ik then u else shrink Ints_t.sub u in - let intv' = norm ik @@ Some (l', u') |> fst in - let range = norm ~suppress_ovwarn:true ik (Some (Ints_t.of_bigint (Size.min_from_bit_range rl), Ints_t.of_bigint (Size.max_from_bit_range rh))) |> fst in - meet ik intv' range - - let refine_with_incl_list ik (intv: t) (incl : (int_t list) option) : t = - match intv, incl with - | None, _ | _, None -> intv - | Some(l, u), Some(ls) -> - let rec min m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> min (Some x) xs | Some m -> if Ints_t.compare m x < 0 then min (Some m) xs else min (Some x) xs in - let rec max m1 ms = match ms with | [] -> m1 | x::xs -> match m1 with - | None -> max (Some x) xs | Some m -> if Ints_t.compare m x > 0 then max (Some m) xs else max (Some x) xs in - match min None ls, max None ls with - | Some m1, Some m2 -> refine_with_interval ik (Some(l, u)) (Some (m1, m2)) - | _, _-> intv - - let project ik p t = t -end - module SOverflowUnlifter (D : SOverflow) : S with type int_t = D.int_t and type t = D.t = struct include D From db37e858017d939c952836e7c0b7f5d3295efe8d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 25 Oct 2024 17:25:13 +0300 Subject: [PATCH 476/566] Add IntDomain exclusions to goblint-lib-modules.py --- scripts/goblint-lib-modules.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index ba25a1403c..eee7b218c5 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -48,6 +48,12 @@ "MessageCategory", # included in Messages "PreValueDomain", # included in ValueDomain + "IntervalDomain", # included in IntDomain + "IntervalSetDomain", # included in IntDomain + "DefExcDomain", # included in IntDomain + "EnumsDomain", # included in IntDomain + "CongruenceDomain", # included in IntDomain + "IntDomTuple", # included in IntDomain "ConfigVersion", "ConfigProfile", From 3970a2fde041afba272b4a93e3a7c4a73f05cdca Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 28 Oct 2024 13:48:12 +0200 Subject: [PATCH 477/566] Add regression test for joining main thread --- .../regression/51-threadjoins/09-join-main.c | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/regression/51-threadjoins/09-join-main.c diff --git a/tests/regression/51-threadjoins/09-join-main.c b/tests/regression/51-threadjoins/09-join-main.c new file mode 100644 index 0000000000..249de594bf --- /dev/null +++ b/tests/regression/51-threadjoins/09-join-main.c @@ -0,0 +1,23 @@ +//PARAM: --set ana.activated[+] threadJoins +#include + +pthread_t mainid; + +int g = 10; + +void *t_fun(void *arg) { + pthread_join(mainid, NULL); + g++; // TODO NORACE + return NULL; +} + + +int main(void) { + mainid = pthread_self(); + + pthread_t id2; + pthread_create(&id2, NULL, t_fun, NULL); + + g++; // TODO NORACE + return 0; +} From 3d048eb1035479f26854a23402bb2aad0e53fd31 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 28 Oct 2024 13:48:37 +0200 Subject: [PATCH 478/566] Add pthread_self support --- src/analyses/base.ml | 9 +++++++++ src/util/library/libraryDesc.ml | 1 + src/util/library/libraryFunctions.ml | 2 +- tests/regression/51-threadjoins/09-join-main.c | 4 ++-- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/analyses/base.ml b/src/analyses/base.ml index cea2c8bcee..e5bcbfede5 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -2651,6 +2651,15 @@ struct | Unknown, "__goblint_assume_join" -> let id = List.hd args in Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st + | ThreadSelf, _ -> + begin match lv, ThreadId.get_current (Analyses.ask_of_ctx ctx) with + | Some lv, `Lifted tid -> + set ~ctx st (eval_lv ~ctx st lv) (Cilfacade.typeOfLval lv) (Thread (ValueDomain.Threads.singleton tid)) + | Some lv, _ -> + invalidate_ret_lv st + | None, _ -> + st + end | Alloca size, _ -> begin match lv with | Some lv -> diff --git a/src/util/library/libraryDesc.ml b/src/util/library/libraryDesc.ml index 80cf86b1e2..6f34de1864 100644 --- a/src/util/library/libraryDesc.ml +++ b/src/util/library/libraryDesc.ml @@ -56,6 +56,7 @@ type special = | ThreadCreate of { thread: Cil.exp; start_routine: Cil.exp; arg: Cil.exp; multiple: bool } | ThreadJoin of { thread: Cil.exp; ret_var: Cil.exp; } | ThreadExit of { ret_val: Cil.exp; } + | ThreadSelf | Globalize of Cil.exp | Signal of Cil.exp | Broadcast of Cil.exp diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index 31fcf0510e..fbcaa4fe60 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -504,7 +504,7 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("pthread_attr_setstacksize", unknown [drop "attr" [w]; drop "stacksize" []]); ("pthread_attr_getscope", unknown [drop "attr" [r]; drop "scope" [w]]); ("pthread_attr_setscope", unknown [drop "attr" [w]; drop "scope" []]); - ("pthread_self", unknown []); + ("pthread_self", special [] ThreadSelf); ("pthread_sigmask", unknown [drop "how" []; drop "set" [r]; drop "oldset" [w]]); ("pthread_setspecific", unknown ~attrs:[InvalidateGlobals] [drop "key" []; drop "value" [w_deep]]); ("pthread_getspecific", unknown ~attrs:[InvalidateGlobals] [drop "key" []]); diff --git a/tests/regression/51-threadjoins/09-join-main.c b/tests/regression/51-threadjoins/09-join-main.c index 249de594bf..1d61eedf89 100644 --- a/tests/regression/51-threadjoins/09-join-main.c +++ b/tests/regression/51-threadjoins/09-join-main.c @@ -7,7 +7,7 @@ int g = 10; void *t_fun(void *arg) { pthread_join(mainid, NULL); - g++; // TODO NORACE + g++; // NORACE return NULL; } @@ -18,6 +18,6 @@ int main(void) { pthread_t id2; pthread_create(&id2, NULL, t_fun, NULL); - g++; // TODO NORACE + g++; // NORACE return 0; } From 01bff20b3fea1bb077c12ba9b0b7d7eba6d27c72 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 28 Oct 2024 14:00:52 +0200 Subject: [PATCH 479/566] Make 51-threadjoins/09-join-main runnable --- tests/regression/51-threadjoins/09-join-main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/regression/51-threadjoins/09-join-main.c b/tests/regression/51-threadjoins/09-join-main.c index 1d61eedf89..196ef8bc00 100644 --- a/tests/regression/51-threadjoins/09-join-main.c +++ b/tests/regression/51-threadjoins/09-join-main.c @@ -1,13 +1,16 @@ //PARAM: --set ana.activated[+] threadJoins #include +#include pthread_t mainid; int g = 10; void *t_fun(void *arg) { - pthread_join(mainid, NULL); + int r = pthread_join(mainid, NULL); // TSan doesn't like this... + printf("j: %d\n", r); g++; // NORACE + printf("t_fun: %d\n", g); return NULL; } @@ -19,5 +22,8 @@ int main(void) { pthread_create(&id2, NULL, t_fun, NULL); g++; // NORACE + printf("main: %d\n", g); + + pthread_exit(NULL); // exit main thread but keep id2 alive, otherwise main returning kills id2 return 0; } From bdc288e9706d3bfd0ae09785b891bc545ac3225e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 28 Oct 2024 15:27:55 +0200 Subject: [PATCH 480/566] Copy 51-threadjoins/09-join-main for plain thread IDs --- .../51-threadjoins/10-join-main-plain.c | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/regression/51-threadjoins/10-join-main-plain.c diff --git a/tests/regression/51-threadjoins/10-join-main-plain.c b/tests/regression/51-threadjoins/10-join-main-plain.c new file mode 100644 index 0000000000..8bcb2b3a79 --- /dev/null +++ b/tests/regression/51-threadjoins/10-join-main-plain.c @@ -0,0 +1,29 @@ +//PARAM: --set ana.activated[+] threadJoins --set ana.thread.domain plain +#include +#include + +pthread_t mainid; + +int g = 10; + +void *t_fun(void *arg) { + int r = pthread_join(mainid, NULL); // TSan doesn't like this... + printf("j: %d\n", r); + g++; // RACE (imprecise by plain thread IDs) + printf("t_fun: %d\n", g); + return NULL; +} + + +int main(void) { + mainid = pthread_self(); + + pthread_t id2; + pthread_create(&id2, NULL, t_fun, NULL); + + g++; // TODO NORACE + printf("main: %d\n", g); + + pthread_exit(NULL); // exit main thread but keep id2 alive, otherwise main returning kills id2 + return 0; +} From 568e97cf331e7b0cd0b7b035dadaa198435867a6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 28 Oct 2024 15:29:04 +0200 Subject: [PATCH 481/566] Improve plain thread ID is_unique --- src/cdomain/value/cdomains/threadIdDomain.ml | 2 +- tests/regression/51-threadjoins/10-join-main-plain.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cdomain/value/cdomains/threadIdDomain.ml b/src/cdomain/value/cdomains/threadIdDomain.ml index fff6734f27..290a6b316b 100644 --- a/src/cdomain/value/cdomains/threadIdDomain.ml +++ b/src/cdomain/value/cdomains/threadIdDomain.ml @@ -86,7 +86,7 @@ struct | ({vname; _}, None) -> List.mem vname @@ GobConfig.get_string_list "mainfun" | _ -> false - let is_unique _ = false (* TODO: should this consider main unique? *) + let is_unique = is_main let may_create _ _ = true let is_must_parent _ _ = false end diff --git a/tests/regression/51-threadjoins/10-join-main-plain.c b/tests/regression/51-threadjoins/10-join-main-plain.c index 8bcb2b3a79..5b2c188bf5 100644 --- a/tests/regression/51-threadjoins/10-join-main-plain.c +++ b/tests/regression/51-threadjoins/10-join-main-plain.c @@ -21,7 +21,7 @@ int main(void) { pthread_t id2; pthread_create(&id2, NULL, t_fun, NULL); - g++; // TODO NORACE + g++; // NORACE printf("main: %d\n", g); pthread_exit(NULL); // exit main thread but keep id2 alive, otherwise main returning kills id2 From 1bb8db120146e371451cd3b1c34bfcf2d44e798b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 30 Oct 2024 15:36:48 +0200 Subject: [PATCH 482/566] Fix plain thread ID is_main unsoundness when ana.thread.include-node is disabled --- src/cdomain/value/cdomains/threadIdDomain.ml | 2 +- .../11-join-main-plain-no-node.c | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/regression/51-threadjoins/11-join-main-plain-no-node.c diff --git a/src/cdomain/value/cdomains/threadIdDomain.ml b/src/cdomain/value/cdomains/threadIdDomain.ml index 290a6b316b..226905ed6f 100644 --- a/src/cdomain/value/cdomains/threadIdDomain.ml +++ b/src/cdomain/value/cdomains/threadIdDomain.ml @@ -83,7 +83,7 @@ struct (v, None) let is_main = function - | ({vname; _}, None) -> List.mem vname @@ GobConfig.get_string_list "mainfun" + | ({vname; _}, None) -> GobConfig.get_bool "ana.thread.include-node" && List.mem vname @@ GobConfig.get_string_list "mainfun" | _ -> false let is_unique = is_main diff --git a/tests/regression/51-threadjoins/11-join-main-plain-no-node.c b/tests/regression/51-threadjoins/11-join-main-plain-no-node.c new file mode 100644 index 0000000000..7f235fd1d8 --- /dev/null +++ b/tests/regression/51-threadjoins/11-join-main-plain-no-node.c @@ -0,0 +1,29 @@ +//PARAM: --set ana.activated[+] threadJoins --set ana.thread.domain plain --disable ana.thread.include-node +#include +#include + +pthread_t mainid; + +int g = 10; + +void *t_fun(void *arg) { + int r = pthread_join(mainid, NULL); // TSan doesn't like this... + printf("j: %d\n", r); + g++; // RACE (imprecise by plain thread IDs) + printf("t_fun: %d\n", g); + return NULL; +} + + +int main(void) { + mainid = pthread_self(); + + pthread_t id2; + pthread_create(&id2, NULL, t_fun, NULL); + + g++; // RACE (imprecise by plain thread IDs not knowing if main is actual main or spawned by program) + printf("main: %d\n", g); + + pthread_exit(NULL); // exit main thread but keep id2 alive, otherwise main returning kills id2 + return 0; +} From 7adeb5a8ac8b45cb01f9649c106606696bb29993 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 31 Oct 2024 18:34:39 +0200 Subject: [PATCH 483/566] Add visitor for finding mallocs/allocs that are not in loops --- src/autoTune.ml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/autoTune.ml b/src/autoTune.ml index f59a10ee8a..7194d1ece7 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -44,6 +44,26 @@ class functionVisitor(calling, calledBy, argLists, dynamicallyCalled) = object DoChildren end +exception Found +class findAllocsNotInLoops = object + inherit nopCilVisitor + + method! vstmt stmt = + match stmt.skind with + | Loop _ -> SkipChildren + | _ -> DoChildren + + method! vinst = function + | Call (_, Lval (Var f, NoOffset), args,_,_) -> + let desc = LibraryFunctions.find f in + begin match desc.special args with + | Malloc _ + | Alloca _ -> raise Found + | _ -> DoChildren + end + | _ -> DoChildren +end + type functionCallMaps = { calling: FunctionSet.t FunctionCallMap.t; calledBy: (FunctionSet.t * int) FunctionCallMap.t; From cb16bae9fc8ee6da2f2e07a703ce93eba62e85c1 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 31 Oct 2024 19:47:38 +0200 Subject: [PATCH 484/566] Set ana.malloc.unique_address_count to 1 when alloc is found in program for noOverflow tasks --- src/autoTune.ml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 7194d1ece7..1eb9aba5fc 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -257,7 +257,11 @@ let focusOnSpecification (spec: Svcomp.Specification.t) = Logs.info "Specification: NoDataRace -> enabling thread analyses \"%s\"" (String.concat ", " notNeccessaryThreadAnalyses); enableAnalyses notNeccessaryThreadAnalyses; | NoOverflow -> (*We focus on integer analysis*) - set_bool "ana.int.def_exc" true + set_bool "ana.int.def_exc" true; + begin + try ignore @@ visitCilFileSameGlobals (new findAllocsNotInLoops) (!Cilfacade.current_file) + with Found -> set_int "ana.malloc.unique_address_count" 1; + end | _ -> () let focusOnSpecification () = From c6a6ee0be729198fd594acf025ffa5b337217c3b Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 31 Oct 2024 19:47:59 +0200 Subject: [PATCH 485/566] Enable autotune.specification --- conf/svcomp.json | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/svcomp.json b/conf/svcomp.json index 50136def50..1e05da580c 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -60,6 +60,7 @@ "singleThreaded", "mallocWrappers", "noRecursiveIntervals", + "specification", "enums", "congruence", "octagon", From 5ceb7205dd2e14ed9bf8135c554dda17e090a92d Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 31 Oct 2024 20:11:28 +0200 Subject: [PATCH 486/566] Separate autotune for each category as options --- conf/svcomp.json | 2 +- src/autoTune.ml | 42 ++++++++++++++++++++++++++-------- src/config/options.schema.json | 8 +++++-- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/conf/svcomp.json b/conf/svcomp.json index 1e05da580c..c1ccaa4ab3 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -60,13 +60,13 @@ "singleThreaded", "mallocWrappers", "noRecursiveIntervals", - "specification", "enums", "congruence", "octagon", "wideningThresholds", "loopUnrollHeuristic", "memsafetySpecification", + "noOverflows", "termination", "tmpSpecialAnalysis" ] diff --git a/src/autoTune.ml b/src/autoTune.ml index 1eb9aba5fc..c54b84d0e9 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -250,13 +250,25 @@ let focusOnTermination (spec: Svcomp.Specification.t) = let focusOnTermination () = List.iter focusOnTermination (Svcomp.Specification.of_option ()) -let focusOnSpecification (spec: Svcomp.Specification.t) = +let focusOnReachSafety (spec: Svcomp.Specification.t) = () + +let focusOnReachSafety () = + List.iter focusOnReachSafety (Svcomp.Specification.of_option ()) + +let focusOnConcurrencySafety (spec: Svcomp.Specification.t) = match spec with - | UnreachCall s -> () | NoDataRace -> (*enable all thread analyses*) Logs.info "Specification: NoDataRace -> enabling thread analyses \"%s\"" (String.concat ", " notNeccessaryThreadAnalyses); enableAnalyses notNeccessaryThreadAnalyses; - | NoOverflow -> (*We focus on integer analysis*) + | _ -> () + +let focusOnConcurrencySafety () = + List.iter focusOnConcurrencySafety (Svcomp.Specification.of_option ()) + +let focusOnNoOverflows (spec: Svcomp.Specification.t) = + match spec with + | NoOverflow -> + (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; begin try ignore @@ visitCilFileSameGlobals (new findAllocsNotInLoops) (!Cilfacade.current_file) @@ -264,8 +276,8 @@ let focusOnSpecification (spec: Svcomp.Specification.t) = end | _ -> () -let focusOnSpecification () = - List.iter focusOnSpecification (Svcomp.Specification.of_option ()) +let focusOnNoOverflows () = + List.iter focusOnNoOverflows (Svcomp.Specification.of_option ()) (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound @@ -513,8 +525,14 @@ let isActivated a = get_bool "ana.autotune.enabled" && List.mem a @@ get_string_ let isTerminationTask () = List.mem Svcomp.Specification.Termination (Svcomp.Specification.of_option ()) -let specificationIsActivated () = - isActivated "specification" && get_string "ana.specification" <> "" +let specificationReachSafetyIsActivated () = + isActivated "reachSafetySpecification" + +let specificationConcurrencySafetyIsActivated () = + isActivated "concurrencySafetySpecification" + +let specificationNoOverflowsIsActivated () = + isActivated "noOverflows" let specificationTerminationIsActivated () = isActivated "termination" @@ -541,8 +559,14 @@ let chooseConfig file = if isActivated "mallocWrappers" then findMallocWrappers (); - if specificationIsActivated () then - focusOnSpecification (); + if specificationReachSafetyIsActivated () then + focusOnReachSafety (); + + if specificationConcurrencySafetyIsActivated () then + focusOnConcurrencySafety (); + + if specificationNoOverflowsIsActivated () then + focusOnNoOverflows (); if isActivated "enums" && hasEnums file then set_bool "ana.int.enums" true; diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 5d87eb51f6..d8a7d3adc7 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -535,7 +535,6 @@ "enum": [ "congruence", "singleThreaded", - "specification", "mallocWrappers", "noRecursiveIntervals", "enums", @@ -545,6 +544,9 @@ "octagon", "wideningThresholds", "memsafetySpecification", + "reachSafetySpecification", + "concurrencySafetySpecification", + "noOverflows", "termination", "tmpSpecialAnalysis" ] @@ -552,7 +554,6 @@ "default": [ "congruence", "singleThreaded", - "specification", "mallocWrappers", "noRecursiveIntervals", "enums", @@ -561,6 +562,9 @@ "octagon", "wideningThresholds", "memsafetySpecification", + "reachSafetySpecification", + "concurrencySafetySpecification", + "noOverflows", "termination", "tmpSpecialAnalysis" ] From 506fb21001924edb008760da682a4e59bc5283dc Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 31 Oct 2024 20:16:09 +0200 Subject: [PATCH 487/566] Refactor: inline functions --- src/autoTune.ml | 21 +++------------------ src/goblint.ml | 2 +- src/maingoblint.ml | 2 +- 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index c54b84d0e9..af7b9ab478 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -525,21 +525,6 @@ let isActivated a = get_bool "ana.autotune.enabled" && List.mem a @@ get_string_ let isTerminationTask () = List.mem Svcomp.Specification.Termination (Svcomp.Specification.of_option ()) -let specificationReachSafetyIsActivated () = - isActivated "reachSafetySpecification" - -let specificationConcurrencySafetyIsActivated () = - isActivated "concurrencySafetySpecification" - -let specificationNoOverflowsIsActivated () = - isActivated "noOverflows" - -let specificationTerminationIsActivated () = - isActivated "termination" - -let specificationMemSafetyIsActivated () = - isActivated "memsafetySpecification" - let chooseConfig file = let factors = collectFactors visitCilFileSameGlobals file in let fileCompplexity = estimateComplexity factors file in @@ -559,13 +544,13 @@ let chooseConfig file = if isActivated "mallocWrappers" then findMallocWrappers (); - if specificationReachSafetyIsActivated () then + if isActivated "reachSafetySpecification" then focusOnReachSafety (); - if specificationConcurrencySafetyIsActivated () then + if isActivated "concurrencySafetySpecification" then focusOnConcurrencySafety (); - if specificationNoOverflowsIsActivated () then + if isActivated "noOverflows" then focusOnNoOverflows (); if isActivated "enums" && hasEnums file then diff --git a/src/goblint.ml b/src/goblint.ml index 6f8f8c20e5..2a0ab3ce0f 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -37,7 +37,7 @@ let main () = Logs.debug "%s" GobSys.command_line; (* When analyzing a termination specification, activate the termination analysis before pre-processing. *) if get_string "ana.specification" <> "" then AutoSoundConfig.enableAnalysesForTerminationSpecification (); - if AutoTune.specificationTerminationIsActivated () then AutoTune.focusOnTermination (); + if AutoTune.isActivated "termination" then AutoTune.focusOnTermination (); let file = lazy (Fun.protect ~finally:GoblintDir.finalize preprocess_parse_merge) in if get_bool "server.enabled" then ( let file = diff --git a/src/maingoblint.ml b/src/maingoblint.ml index 95849bce36..cb81ea0b86 100644 --- a/src/maingoblint.ml +++ b/src/maingoblint.ml @@ -203,7 +203,7 @@ let handle_options () = Sys.set_signal (GobSys.signal_of_string (get_string "dbg.solver-signal")) Signal_ignore; (* Ignore solver-signal before solving (e.g. MyCFG), otherwise exceptions self-signal the default, which crashes instead of printing backtrace. *) if get_string "ana.specification" <> "" then AutoSoundConfig.enableAnalysesForMemSafetySpecification (); - if AutoTune.specificationMemSafetyIsActivated () then + if AutoTune.isActivated "memsafetySpecification" then AutoTune.focusOnMemSafetySpecification (); AfterConfig.run (); Cilfacade.init_options (); From 2508f1ac655401a13c4831ddfc008be72dacd977 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 31 Oct 2024 20:20:28 +0200 Subject: [PATCH 488/566] Refactor: generalize repetitive function definition --- src/autoTune.ml | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index af7b9ab478..8dfcc6480e 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -250,22 +250,16 @@ let focusOnTermination (spec: Svcomp.Specification.t) = let focusOnTermination () = List.iter focusOnTermination (Svcomp.Specification.of_option ()) -let focusOnReachSafety (spec: Svcomp.Specification.t) = () +let reachSafety (spec: Svcomp.Specification.t) = () -let focusOnReachSafety () = - List.iter focusOnReachSafety (Svcomp.Specification.of_option ()) - -let focusOnConcurrencySafety (spec: Svcomp.Specification.t) = +let concurrencySafety (spec: Svcomp.Specification.t) = match spec with | NoDataRace -> (*enable all thread analyses*) Logs.info "Specification: NoDataRace -> enabling thread analyses \"%s\"" (String.concat ", " notNeccessaryThreadAnalyses); enableAnalyses notNeccessaryThreadAnalyses; | _ -> () -let focusOnConcurrencySafety () = - List.iter focusOnConcurrencySafety (Svcomp.Specification.of_option ()) - -let focusOnNoOverflows (spec: Svcomp.Specification.t) = +let noOverflows (spec: Svcomp.Specification.t) = match spec with | NoOverflow -> (*We focus on integer analysis*) @@ -276,8 +270,8 @@ let focusOnNoOverflows (spec: Svcomp.Specification.t) = end | _ -> () -let focusOnNoOverflows () = - List.iter focusOnNoOverflows (Svcomp.Specification.of_option ()) +let focusOn (f : SvcompSpec.t -> unit) = + List.iter f (Svcomp.Specification.of_option ()) (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound @@ -544,14 +538,11 @@ let chooseConfig file = if isActivated "mallocWrappers" then findMallocWrappers (); - if isActivated "reachSafetySpecification" then - focusOnReachSafety (); + if isActivated "reachSafetySpecification" then focusOn reachSafety; - if isActivated "concurrencySafetySpecification" then - focusOnConcurrencySafety (); + if isActivated "concurrencySafetySpecification" then focusOn concurrencySafety; - if isActivated "noOverflows" then - focusOnNoOverflows (); + if isActivated "noOverflows" then focusOn noOverflows; if isActivated "enums" && hasEnums file then set_bool "ana.int.enums" true; From 0b6f98174c544631c4a1cc80f47c19ac75e992dd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 1 Nov 2024 12:45:19 +0200 Subject: [PATCH 489/566] Add semgrep rules for finding exists/forall-like folds --- .semgrep/fold.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .semgrep/fold.yml diff --git a/.semgrep/fold.yml b/.semgrep/fold.yml new file mode 100644 index 0000000000..8e4739791f --- /dev/null +++ b/.semgrep/fold.yml @@ -0,0 +1,26 @@ +rules: + - id: fold-exists + patterns: + - pattern-either: + - pattern: $D.fold ... false + - pattern: $D.fold_left ... false + - pattern: $D.fold_right ... false + - pattern: fold ... false + - pattern: fold_left ... false + - pattern: fold_right ... false + message: consider replacing fold with exists + languages: [ocaml] + severity: WARNING + + - id: fold-for_all + patterns: + - pattern-either: + - pattern: $D.fold ... true + - pattern: $D.fold_left ... true + - pattern: $D.fold_right ... true + - pattern: fold ... true + - pattern: fold_left ... true + - pattern: fold_right ... true + message: consider replacing fold with for_all + languages: [ocaml] + severity: WARNING From 513662ce92e14b6a8fe25a92c6b47622de858dfb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 1 Nov 2024 12:55:08 +0200 Subject: [PATCH 490/566] Fix or suppress semgrep fold-exists/for_all warnings --- src/analyses/basePriv.ml | 12 ++++++------ src/domain/partitionDomain.ml | 6 +++--- src/incremental/compareCFG.ml | 2 +- src/solver/td3.ml | 2 +- src/witness/witness.ml | 8 ++++---- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 946b8f8cc5..e69757c7a8 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1040,11 +1040,11 @@ struct let s = MustLockset.remove m (current_lockset ask) in let t = current_thread ask in let side_cpa = CPA.filter (fun x _ -> - GWeak.fold (fun s' tm acc -> + GWeak.exists (fun s' tm -> (* TODO: swap 2^M and T partitioning for lookup by t here first? *) let v = ThreadMap.find t tm in - (MustLockset.mem m s' && not (VD.is_bot v)) || acc - ) (G.weak (getg (V.global x))) false + (MustLockset.mem m s' && not (VD.is_bot v)) + ) (G.weak (getg (V.global x))) ) st.cpa in sideg (V.mutex m) (G.create_sync (GSync.singleton s side_cpa)); @@ -1098,9 +1098,9 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let s = MustLockset.remove m (current_lockset ask) in let side_cpa = CPA.filter (fun x _ -> - GWeak.fold (fun s' v acc -> - (MustLockset.mem m s' && not (VD.is_bot v)) || acc - ) (G.weak (getg (V.global x))) false + GWeak.exists (fun s' v -> + (MustLockset.mem m s' && not (VD.is_bot v)) + ) (G.weak (getg (V.global x))) ) st.cpa in sideg (V.mutex m) (G.create_sync (GSync.singleton s side_cpa)); diff --git a/src/domain/partitionDomain.ml b/src/domain/partitionDomain.ml index 9675e9bfce..316f4fb705 100644 --- a/src/domain/partitionDomain.ml +++ b/src/domain/partitionDomain.ml @@ -31,10 +31,10 @@ struct let meet _ _ = failwith "PartitonDomain.Set.meet: unsound" let collapse (s1:t) (s2:t): bool = - let f vf2 res = - res || exists (fun vf1 -> S.collapse vf1 vf2) s1 + let f vf2 = + exists (fun vf1 -> S.collapse vf1 vf2) s1 in - fold f s2 false + exists f s2 let add e s = join s (singleton e) diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 6c314ef7c9..a663b80833 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -131,7 +131,7 @@ let reexamine f1 f2 (same : biDirectionNodeMap) (diffNodes1 : unit NH.t) (module false end in let cond n2 = Node.equal n2 (FunctionEntry f2) || check_all_nodes_in_same (List.map snd (CfgNew.prev n2)) n2 in - let forall = NH.fold (fun n2 n1 acc -> acc && cond n2) same.node2to1 true in + let forall = NH.fold (fun n2 n1 acc -> acc && cond n2) same.node2to1 true in (* nosemgrep: fold-for_all *) (* cond does side effects *) if not forall then repeat () in repeat (); NH.to_seq same.node1to2, NH.to_seq_keys diffNodes1 diff --git a/src/solver/td3.ml b/src/solver/td3.ml index c7bec621e3..049dce2a0d 100644 --- a/src/solver/td3.ml +++ b/src/solver/td3.ml @@ -289,7 +289,7 @@ module Base = destabilize_vs y || b || was_stable && List.mem_cmp S.Var.compare y vs else true - ) w false + ) w false (* nosemgrep: fold-exists *) (* does side effects *) and solve ?reuse_eq x phase = if tracing then trace "sol2" "solve %a, phase: %s, called: %b, stable: %b, wpoint: %b" S.Var.pretty_trace x (show_phase phase) (HM.mem called x) (HM.mem stable x) (HM.mem wpoint x); init x; diff --git a/src/witness/witness.ml b/src/witness/witness.ml index 5da46a1011..bb70c3319f 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -342,14 +342,14 @@ struct | UnreachCall _ -> (* error function name is globally known through Svcomp.task *) let is_unreach_call = - LHT.fold (fun (n, c) v acc -> + LHT.for_all (fun (n, c) v -> match n with (* FunctionEntry isn't used for extern __VERIFIER_error... *) | FunctionEntry f when Svcomp.is_error_function f.svar -> let is_dead = Spec.D.is_bot v in - acc && is_dead - | _ -> acc - ) lh true + is_dead + | _ -> true + ) lh in if is_unreach_call then ( From 62cb655ab97c9fe2bf2967ba68af5d6247bb3f8d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 1 Nov 2024 12:56:32 +0200 Subject: [PATCH 491/566] Extend semgrep tracing rule for abbreviated module name --- .semgrep/tracing.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.semgrep/tracing.yml b/.semgrep/tracing.yml index 061b3efa0d..9c7813a7e8 100644 --- a/.semgrep/tracing.yml +++ b/.semgrep/tracing.yml @@ -8,8 +8,16 @@ rules: - pattern: Messages.tracec - pattern: Messages.traceu - pattern: Messages.traceli + - pattern: M.trace + - pattern: M.tracel + - pattern: M.tracei + - pattern: M.tracec + - pattern: M.traceu + - pattern: M.traceli - pattern-not-inside: if Messages.tracing then ... - pattern-not-inside: if Messages.tracing && ... then ... + - pattern-not-inside: if M.tracing then ... + - pattern-not-inside: if M.tracing && ... then ... message: trace functions should only be called if tracing is enabled at compile time languages: [ocaml] severity: WARNING From c0d51c321042153900101d7cf92b489cf119e425 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Sun, 3 Nov 2024 17:24:42 +0100 Subject: [PATCH 492/566] Fix `thread` for non-unique spawns --- src/analyses/threadAnalysis.ml | 10 +++--- .../40-threadid/12-multiple-created-only.c | 26 ++++++++++++++++ tests/regression/40-threadid/13-no-crash.c | 31 +++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 tests/regression/40-threadid/12-multiple-created-only.c create mode 100644 tests/regression/40-threadid/13-no-crash.c diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index 07f46e915d..a67c26092c 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -95,9 +95,7 @@ struct let startstate v = D.bot () let threadenter ctx ~multiple lval f args = - if multiple then - (let tid = ThreadId.get_current_unlift (Analyses.ask_of_ctx ctx) in - ctx.sideg tid (true, TS.bot (), false)); + (* ctx is of creator, side-effects to denote non-uniqueness are performed in threadspawn *) [D.bot ()] let threadspawn ctx ~multiple lval f args fctx = @@ -106,9 +104,9 @@ struct let repeated = D.mem tid ctx.local in let eff = match creator with - | `Lifted ctid -> (repeated, TS.singleton ctid, false) - | `Top -> (true, TS.bot (), false) - | `Bot -> (false, TS.bot (), false) + | `Lifted ctid -> (repeated || multiple, TS.singleton ctid, false) + | `Top -> (true, TS.bot (), false) + | `Bot -> (false || multiple, TS.bot (), false) in ctx.sideg tid eff; D.join ctx.local (D.singleton tid) diff --git a/tests/regression/40-threadid/12-multiple-created-only.c b/tests/regression/40-threadid/12-multiple-created-only.c new file mode 100644 index 0000000000..e65021caaf --- /dev/null +++ b/tests/regression/40-threadid/12-multiple-created-only.c @@ -0,0 +1,26 @@ +// PARAM: --set ana.activated[+] thread --set ana.activated[+] threadid --set ana.thread.domain plain + +#include +#include + +int myglobal; +int myglobal2; + +void* bla(void *arg) { + // This is created multiple times, it should race with itself + myglobal = 10; //RACE + return NULL; +} + +void* other(void) { + // This is created only once, it should not be marked as non-unique + unknown(bla); + myglobal2 = 30; //NORACE +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, other, NULL); + + return 0; +} diff --git a/tests/regression/40-threadid/13-no-crash.c b/tests/regression/40-threadid/13-no-crash.c new file mode 100644 index 0000000000..c9a6c7d88b --- /dev/null +++ b/tests/regression/40-threadid/13-no-crash.c @@ -0,0 +1,31 @@ +// PARAM: --set ana.context.gas_value 0 --set ana.activated[+] thread --set ana.activated[+] threadid + +#include +#include + +int myglobal; +int myglobal2; + +void *t_flurb(void *arg) { + myglobal=40; //RACE + return NULL; +} + +void* bla(void *arg) { + return NULL; +} + +void *t_fun(void *arg) { + unknown(t_flurb); // NOCRASH + return NULL; +} + +int main(void) { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + pthread_create(&id, NULL, t_fun, NULL); + + unknown(bla); + + return 0; +} From 39d0a8aa8664b9296645a4b69f639179a7224efd Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 4 Nov 2024 10:29:14 +0200 Subject: [PATCH 493/566] Use HM.exists instead of HM.fold in td3 (closes #1618) --- src/solver/td3.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver/td3.ml b/src/solver/td3.ml index 049dce2a0d..3cab3cf7f7 100644 --- a/src/solver/td3.ml +++ b/src/solver/td3.ml @@ -49,7 +49,7 @@ module Base = open SolverBox.Warrow (S.Dom) include Generic.SolverStats (S) (HM) module VS = Set.Make (S.Var) - let exists_key f hm = HM.fold (fun k _ a -> a || f k) hm false + let exists_key f hm = HM.exists (fun k _ -> f k) hm type solver_data = { st: (S.Var.t * S.Dom.t) list; (* needed to destabilize start functions if their start state changed because of some changed global initializer *) From f7184c06244e8919638a3870a7922fd37a1e817b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 4 Nov 2024 11:08:17 +0200 Subject: [PATCH 494/566] Print innermost backtrace mark for uncaught exception even with backtrace printing off --- src/util/backtrace/goblint_backtrace.ml | 9 ++++++++- src/util/backtrace/goblint_backtrace.mli | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/util/backtrace/goblint_backtrace.ml b/src/util/backtrace/goblint_backtrace.ml index 513753bddb..21d150a470 100644 --- a/src/util/backtrace/goblint_backtrace.ml +++ b/src/util/backtrace/goblint_backtrace.ml @@ -76,12 +76,19 @@ let print_marktrace oc e = Printf.fprintf oc "Marked with %s\n" (mark_to_string m) ) ms +let print_innermost_mark oc e = + match find_marks e with + | m :: _ -> Printf.fprintf oc "Marked with %s\n" (mark_to_string m) + | [] -> () + let () = Printexc.set_uncaught_exception_handler (fun e bt -> (* Copied & modified from Printexc.default_uncaught_exception_handler. *) Printf.eprintf "Fatal error: exception %s\n" (Printexc.to_string e); (* nosemgrep: print-not-logging *) if Printexc.backtrace_status () then - print_marktrace stderr e; + print_marktrace stderr e + else + print_innermost_mark stderr e; Printexc.print_raw_backtrace stderr bt; flush stderr ) diff --git a/src/util/backtrace/goblint_backtrace.mli b/src/util/backtrace/goblint_backtrace.mli index e53bfd826a..ee7052122e 100644 --- a/src/util/backtrace/goblint_backtrace.mli +++ b/src/util/backtrace/goblint_backtrace.mli @@ -32,5 +32,10 @@ val print_marktrace: out_channel -> exn -> unit Used by default for uncaught exceptions. *) +val print_innermost_mark: out_channel -> exn -> unit +(** Print innermost mark of an exception. + + Used by default for uncaught exceptions. *) + val find_marks: exn -> mark list (** Find all marks of an exception. *) From 1bb50df301ddfd63b9a15677c3bf6f9cf5d026ff Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Nov 2024 10:38:13 +0100 Subject: [PATCH 495/566] Use multiple also in bot case Co-authored-by: Simmo Saan --- src/analyses/threadAnalysis.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index a67c26092c..435e1a6afe 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -106,7 +106,7 @@ struct match creator with | `Lifted ctid -> (repeated || multiple, TS.singleton ctid, false) | `Top -> (true, TS.bot (), false) - | `Bot -> (false || multiple, TS.bot (), false) + | `Bot -> (multiple, TS.bot (), false) in ctx.sideg tid eff; D.join ctx.local (D.singleton tid) From eb149f9520a05664bb1d7addd90d6a23f44f5832 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Nov 2024 10:39:24 +0100 Subject: [PATCH 496/566] Move tests --- .../29-multiple-created-only.c} | 0 .../{40-threadid/13-no-crash.c => 10-synch/30-no-crash.c} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/regression/{40-threadid/12-multiple-created-only.c => 10-synch/29-multiple-created-only.c} (100%) rename tests/regression/{40-threadid/13-no-crash.c => 10-synch/30-no-crash.c} (100%) diff --git a/tests/regression/40-threadid/12-multiple-created-only.c b/tests/regression/10-synch/29-multiple-created-only.c similarity index 100% rename from tests/regression/40-threadid/12-multiple-created-only.c rename to tests/regression/10-synch/29-multiple-created-only.c diff --git a/tests/regression/40-threadid/13-no-crash.c b/tests/regression/10-synch/30-no-crash.c similarity index 100% rename from tests/regression/40-threadid/13-no-crash.c rename to tests/regression/10-synch/30-no-crash.c From 7c4581b400f242bb005e9d329e7594231e156708 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 5 Nov 2024 12:35:34 +0200 Subject: [PATCH 497/566] Remove empty focusOn reachSafety and its (yet) pointless option reachSafetySpecification --- src/autoTune.ml | 4 ---- src/config/options.schema.json | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 8dfcc6480e..3db3729d0d 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -250,8 +250,6 @@ let focusOnTermination (spec: Svcomp.Specification.t) = let focusOnTermination () = List.iter focusOnTermination (Svcomp.Specification.of_option ()) -let reachSafety (spec: Svcomp.Specification.t) = () - let concurrencySafety (spec: Svcomp.Specification.t) = match spec with | NoDataRace -> (*enable all thread analyses*) @@ -538,8 +536,6 @@ let chooseConfig file = if isActivated "mallocWrappers" then findMallocWrappers (); - if isActivated "reachSafetySpecification" then focusOn reachSafety; - if isActivated "concurrencySafetySpecification" then focusOn concurrencySafety; if isActivated "noOverflows" then focusOn noOverflows; diff --git a/src/config/options.schema.json b/src/config/options.schema.json index d8a7d3adc7..746b950547 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -544,7 +544,6 @@ "octagon", "wideningThresholds", "memsafetySpecification", - "reachSafetySpecification", "concurrencySafetySpecification", "noOverflows", "termination", @@ -562,7 +561,6 @@ "octagon", "wideningThresholds", "memsafetySpecification", - "reachSafetySpecification", "concurrencySafetySpecification", "noOverflows", "termination", From 53f540da9d9d1814622255d08c5342234ffe1861 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 5 Nov 2024 12:36:45 +0200 Subject: [PATCH 498/566] Add autotune 'noOverflows' option --- conf/svcomp-validate.json | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/svcomp-validate.json b/conf/svcomp-validate.json index 8e11fee7f5..64564f480f 100644 --- a/conf/svcomp-validate.json +++ b/conf/svcomp-validate.json @@ -67,6 +67,7 @@ "wideningThresholds", "loopUnrollHeuristic", "memsafetySpecification", + "noOverflows", "termination", "tmpSpecialAnalysis" ] From 4e7bfaeb472c4e60c4d84bb8ab7258297672f810 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 5 Nov 2024 12:47:45 +0200 Subject: [PATCH 499/566] Detect mallocs in loops instead of detecting mallocs outside of loops --- src/autoTune.ml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 3db3729d0d..56fdfcca25 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -45,12 +45,14 @@ class functionVisitor(calling, calledBy, argLists, dynamicallyCalled) = object end exception Found -class findAllocsNotInLoops = object +class findAllocsInLoops = object inherit nopCilVisitor + val mutable inloop = false + method! vstmt stmt = match stmt.skind with - | Loop _ -> SkipChildren + | Loop _ -> inloop <- true; DoChildren | _ -> DoChildren method! vinst = function @@ -58,7 +60,7 @@ class findAllocsNotInLoops = object let desc = LibraryFunctions.find f in begin match desc.special args with | Malloc _ - | Alloca _ -> raise Found + | Alloca _ when inloop -> raise Found | _ -> DoChildren end | _ -> DoChildren @@ -263,8 +265,10 @@ let noOverflows (spec: Svcomp.Specification.t) = (*We focus on integer analysis*) set_bool "ana.int.def_exc" true; begin - try ignore @@ visitCilFileSameGlobals (new findAllocsNotInLoops) (!Cilfacade.current_file) - with Found -> set_int "ana.malloc.unique_address_count" 1; + try + ignore @@ visitCilFileSameGlobals (new findAllocsInLoops) (!Cilfacade.current_file); + set_int "ana.malloc.unique_address_count" 1 + with Found -> set_int "ana.malloc.unique_address_count" 0; end | _ -> () From fa31b55e85b54355b0d61c0c4f46272527ec4ea2 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Tue, 5 Nov 2024 15:44:46 +0200 Subject: [PATCH 500/566] Reset inloop after processing children --- src/autoTune.ml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 56fdfcca25..cae6c62c68 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -51,8 +51,13 @@ class findAllocsInLoops = object val mutable inloop = false method! vstmt stmt = + let outOfLoop stmt = + match stmt.skind with + | Loop _ -> inloop <- false; stmt + | _ -> stmt + in match stmt.skind with - | Loop _ -> inloop <- true; DoChildren + | Loop _ -> inloop <- true; ChangeDoChildrenPost(stmt, outOfLoop) | _ -> DoChildren method! vinst = function From b1095fbd71b7360e1a6d7a7d8b9bcc3b790b3bef Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Nov 2024 11:32:06 +0200 Subject: [PATCH 501/566] Add more precise YAML witness generation summary --- src/witness/yamlWitness.ml | 13 ++++++++++++ .../03-practical/35-base-mutex-macos.t | 3 +++ tests/regression/13-privatized/01-priv_nr.t | 9 ++++++++ .../regression/36-apron/12-traces-min-rpb1.t | 3 +++ tests/regression/36-apron/52-queuesize.t | 6 ++++++ .../11-unrolled-loop-invariant.t | 3 +++ tests/regression/56-witness/05-prec-problem.t | 3 +++ .../56-witness/08-witness-all-locals.t | 6 ++++++ .../56-witness/46-top-bool-invariant.t | 21 +++++++++++++++++++ .../56-witness/47-top-int-invariant.t | 21 +++++++++++++++++++ tests/regression/cfg/foo.t/run.t | 3 +++ tests/regression/cfg/issue-1356.t/run.t | 3 +++ tests/regression/cfg/loops.t/run.t | 3 +++ tests/regression/cfg/pr-758.t/run.t | 3 +++ tests/regression/witness/int.t/run.t | 3 +++ tests/regression/witness/typedef.t/run.t | 6 ++++++ 16 files changed, 109 insertions(+) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 2bdd2ced4c..bc31797688 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -249,6 +249,11 @@ struct let entries = [] in + let cnt_loop_invariant = ref 0 in + let cnt_location_invariant = ref 0 in + let cnt_flow_insensitive_invariant = ref 0 in + (* TODO: precondition invariants? *) + (* Generate location invariants (without precondition) *) let entries = if entry_type_enabled YamlWitnessType.LocationInvariant.entry_type then ( @@ -268,6 +273,7 @@ struct List.fold_left (fun acc inv -> let invariant = Entry.invariant (CilType.Exp.show inv) in let entry = Entry.location_invariant ~task ~location ~invariant in + incr cnt_location_invariant; entry :: acc ) acc invs | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) @@ -297,6 +303,7 @@ struct List.fold_left (fun acc inv -> let invariant = Entry.invariant (CilType.Exp.show inv) in let entry = Entry.loop_invariant ~task ~location ~invariant in + incr cnt_loop_invariant; entry :: acc ) acc invs | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) @@ -322,6 +329,7 @@ struct List.fold_left (fun acc inv -> let invariant = Entry.invariant (CilType.Exp.show inv) in let entry = Entry.flow_insensitive_invariant ~task ~invariant in + incr cnt_flow_insensitive_invariant; entry :: acc ) acc invs | `Bot | `Top -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) @@ -459,6 +467,7 @@ struct List.fold_left (fun acc inv -> let invariant = CilType.Exp.show inv in let invariant = Entry.location_invariant' ~location ~invariant in + incr cnt_location_invariant; invariant :: acc ) acc invs | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) @@ -488,6 +497,7 @@ struct List.fold_left (fun acc inv -> let invariant = CilType.Exp.show inv in let invariant = Entry.loop_invariant' ~location ~invariant in + incr cnt_loop_invariant; invariant :: acc ) acc invs | `Bot | `Top -> (* TODO: 0 for bot (dead code)? *) @@ -512,6 +522,9 @@ 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 "location invariants: %d" !cnt_location_invariant, None); + (Pretty.dprintf "loop invariants: %d" !cnt_loop_invariant, None); + (Pretty.dprintf "flow-insensitive invariants: %d" !cnt_flow_insensitive_invariant, None); (Pretty.dprintf "total generation entries: %d" (List.length yaml_entries), None); ]; diff --git a/tests/regression/03-practical/35-base-mutex-macos.t b/tests/regression/03-practical/35-base-mutex-macos.t index 9e5f36d337..1d8a184d4c 100644 --- a/tests/regression/03-practical/35-base-mutex-macos.t +++ b/tests/regression/03-practical/35-base-mutex-macos.t @@ -4,6 +4,9 @@ dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 1 There should be no invariants about __sig. diff --git a/tests/regression/13-privatized/01-priv_nr.t b/tests/regression/13-privatized/01-priv_nr.t index bbc285098a..0186709027 100644 --- a/tests/regression/13-privatized/01-priv_nr.t +++ b/tests/regression/13-privatized/01-priv_nr.t @@ -10,6 +10,9 @@ dead: 0 total lines: 19 [Info][Witness] witness generation summary: + location invariants: 3 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 3 [Info][Race] Memory locations race summary: safe: 1 @@ -64,6 +67,9 @@ dead: 0 total lines: 19 [Info][Witness] witness generation summary: + location invariants: 3 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 3 [Info][Race] Memory locations race summary: safe: 1 @@ -118,6 +124,9 @@ dead: 0 total lines: 19 [Info][Witness] witness generation summary: + location invariants: 3 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 3 [Info][Race] Memory locations race summary: safe: 1 diff --git a/tests/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t index 5060f505d9..d0cebd6d1c 100644 --- a/tests/regression/36-apron/12-traces-min-rpb1.t +++ b/tests/regression/36-apron/12-traces-min-rpb1.t @@ -13,6 +13,9 @@ write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:14:3-14:8) read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:27:3-27:26) [Info][Witness] witness generation summary: + location invariants: 3 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 3 [Info][Race] Memory locations race summary: safe: 0 diff --git a/tests/regression/36-apron/52-queuesize.t b/tests/regression/36-apron/52-queuesize.t index 62851f2ec9..f0a977891a 100644 --- a/tests/regression/36-apron/52-queuesize.t +++ b/tests/regression/36-apron/52-queuesize.t @@ -37,6 +37,9 @@ Without diff-box: [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (52-queuesize.c:56:10-56:11) [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (52-queuesize.c:78:12-78:13) [Info][Witness] witness generation summary: + location invariants: 8 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 8 [Info][Race] Memory locations race summary: safe: 3 @@ -173,6 +176,9 @@ With diff-box: [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (52-queuesize.c:56:10-56:11) [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (52-queuesize.c:78:12-78:13) [Info][Witness] witness generation summary: + location invariants: 6 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 6 [Info][Race] Memory locations race summary: safe: 3 diff --git a/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t b/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t index 3a3b7c43cf..860ffae3bd 100644 --- a/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t +++ b/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t @@ -211,6 +211,9 @@ [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) [Info][Witness] witness generation summary: + location invariants: 11 + loop invariants: 5 + flow-insensitive invariants: 0 total generation entries: 16 $ yamlWitnessStrip < witness.yml diff --git a/tests/regression/56-witness/05-prec-problem.t b/tests/regression/56-witness/05-prec-problem.t index 733f16269e..51f92ca203 100644 --- a/tests/regression/56-witness/05-prec-problem.t +++ b/tests/regression/56-witness/05-prec-problem.t @@ -6,6 +6,9 @@ total lines: 13 [Warning][Deadcode][CWE-570] condition '0' (possibly inserted by CIL) is always false (05-prec-problem.c:13:12-13:13) [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 6 TODO: Don't generate duplicate entries from each context: should have generated just 3. diff --git a/tests/regression/56-witness/08-witness-all-locals.t b/tests/regression/56-witness/08-witness-all-locals.t index fc4462201d..fe6aefefbd 100644 --- a/tests/regression/56-witness/08-witness-all-locals.t +++ b/tests/regression/56-witness/08-witness-all-locals.t @@ -4,6 +4,9 @@ dead: 0 total lines: 4 [Info][Witness] witness generation summary: + location invariants: 3 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 3 $ yamlWitnessStrip < witness.yml @@ -50,6 +53,9 @@ Fewer entries are emitted if locals from nested block scopes are excluded: dead: 0 total lines: 4 [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 2 $ yamlWitnessStrip < witness.yml diff --git a/tests/regression/56-witness/46-top-bool-invariant.t b/tests/regression/56-witness/46-top-bool-invariant.t index 741b00966f..be41ef58f2 100644 --- a/tests/regression/56-witness/46-top-bool-invariant.t +++ b/tests/regression/56-witness/46-top-bool-invariant.t @@ -6,6 +6,9 @@ def_exc only: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 2 $ yamlWitnessStrip < witness.yml @@ -40,6 +43,9 @@ interval only: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 2 $ yamlWitnessStrip < witness.yml @@ -74,6 +80,9 @@ enums only: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 1 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 1 $ yamlWitnessStrip < witness.yml @@ -97,6 +106,9 @@ congruence only: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 0 $ yamlWitnessStrip < witness.yml @@ -110,6 +122,9 @@ interval_set only: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 2 $ yamlWitnessStrip < witness.yml @@ -144,6 +159,9 @@ all: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 1 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 1 $ yamlWitnessStrip < witness.yml @@ -167,6 +185,9 @@ all without inexact-type-bounds: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 0 $ yamlWitnessStrip < witness.yml diff --git a/tests/regression/56-witness/47-top-int-invariant.t b/tests/regression/56-witness/47-top-int-invariant.t index cdfe65673f..35d5978c00 100644 --- a/tests/regression/56-witness/47-top-int-invariant.t +++ b/tests/regression/56-witness/47-top-int-invariant.t @@ -6,6 +6,9 @@ def_exc only: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 2 $ yamlWitnessStrip < witness.yml @@ -40,6 +43,9 @@ interval only: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 2 $ yamlWitnessStrip < witness.yml @@ -74,6 +80,9 @@ enums only: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 2 $ yamlWitnessStrip < witness.yml @@ -108,6 +117,9 @@ congruence only: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 0 $ yamlWitnessStrip < witness.yml @@ -121,6 +133,9 @@ interval_set only: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 2 $ yamlWitnessStrip < witness.yml @@ -155,6 +170,9 @@ all: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 2 $ yamlWitnessStrip < witness.yml @@ -189,6 +207,9 @@ all without inexact-type-bounds: dead: 0 total lines: 2 [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 0 $ yamlWitnessStrip < witness.yml diff --git a/tests/regression/cfg/foo.t/run.t b/tests/regression/cfg/foo.t/run.t index cd890b7a19..19873d7540 100644 --- a/tests/regression/cfg/foo.t/run.t +++ b/tests/regression/cfg/foo.t/run.t @@ -67,6 +67,9 @@ total lines: 6 [Warning][Deadcode][CWE-571] condition 'a > 0' (possibly inserted by CIL) is always true (foo.c:3:10-3:20) [Info][Witness] witness generation summary: + location invariants: 8 + loop invariants: 2 + flow-insensitive invariants: 0 total generation entries: 10 $ yamlWitnessStrip < witness.yml diff --git a/tests/regression/cfg/issue-1356.t/run.t b/tests/regression/cfg/issue-1356.t/run.t index aee9456b61..d1fcb3c7ef 100644 --- a/tests/regression/cfg/issue-1356.t/run.t +++ b/tests/regression/cfg/issue-1356.t/run.t @@ -99,6 +99,9 @@ dead: 0 total lines: 13 [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 0 $ yamlWitnessStrip < witness.yml diff --git a/tests/regression/cfg/loops.t/run.t b/tests/regression/cfg/loops.t/run.t index 6596e7b4a4..1fd19b41fe 100644 --- a/tests/regression/cfg/loops.t/run.t +++ b/tests/regression/cfg/loops.t/run.t @@ -219,6 +219,9 @@ dead: 0 total lines: 20 [Info][Witness] witness generation summary: + location invariants: 32 + loop invariants: 21 + flow-insensitive invariants: 0 total generation entries: 53 $ yamlWitnessStrip < witness.yml diff --git a/tests/regression/cfg/pr-758.t/run.t b/tests/regression/cfg/pr-758.t/run.t index 58bbb88ce4..082c63e860 100644 --- a/tests/regression/cfg/pr-758.t/run.t +++ b/tests/regression/cfg/pr-758.t/run.t @@ -93,6 +93,9 @@ dead: 0 total lines: 6 [Info][Witness] witness generation summary: + location invariants: 10 + loop invariants: 2 + flow-insensitive invariants: 0 total generation entries: 12 $ yamlWitnessStrip < witness.yml diff --git a/tests/regression/witness/int.t/run.t b/tests/regression/witness/int.t/run.t index 6b4784ce32..9448ac7855 100644 --- a/tests/regression/witness/int.t/run.t +++ b/tests/regression/witness/int.t/run.t @@ -7,6 +7,9 @@ dead: 0 total lines: 10 [Info][Witness] witness generation summary: + location invariants: 3 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 3 $ yamlWitnessStrip < witness.yml diff --git a/tests/regression/witness/typedef.t/run.t b/tests/regression/witness/typedef.t/run.t index 55dcc1f911..f9fac0c743 100644 --- a/tests/regression/witness/typedef.t/run.t +++ b/tests/regression/witness/typedef.t/run.t @@ -4,6 +4,9 @@ dead: 0 total lines: 6 [Info][Witness] witness generation summary: + location invariants: 13 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 13 $ yamlWitnessStrip < witness.yml @@ -157,6 +160,9 @@ dead: 0 total lines: 6 [Info][Witness] witness generation summary: + location invariants: 14 + loop invariants: 0 + flow-insensitive invariants: 0 total generation entries: 14 $ yamlWitnessStrip < witness.yml From 77190828a810819b5b607c59d1553fc713b1be9d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Nov 2024 11:45:13 +0200 Subject: [PATCH 502/566] Add witness.yaml.strict option description --- src/config/options.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 447290b44d..9c1f9e1e76 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -2659,7 +2659,7 @@ }, "strict": { "title": "witness.yaml.strict", - "description": "", + "description": "Fail YAML witness validation if there's an error/unsupported/disabled entry.", "type": "boolean", "default": false }, From 546a8d04ede0d6646e1d5b20095c0ae5e2f0a78b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Nov 2024 11:49:17 +0200 Subject: [PATCH 503/566] Update YAML witness validation result for refutation under new scoring schema --- src/witness/yamlWitness.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index bc31797688..1a8c536da5 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -892,7 +892,9 @@ struct | true when !cnt_disabled > 0 -> Error "witness disabled" | _ when !cnt_refuted > 0 -> - Ok (Svcomp.Result.False None) + (* Refuted only when assuming the invariant is reachable. *) + (* Ok (Svcomp.Result.False None) *) (* Wasn't a problem because valid*->correctness->false gave 0 points under old validator track scoring schema: https://doi.org/10.1007/978-3-031-22308-2_8. *) + Ok Svcomp.Result.Unknown (* Now valid*->correctness->false gives 1p (negative) points under new validator track scoring schema: https://doi.org/10.1007/978-3-031-57256-2_15. *) | _ when !cnt_unconfirmed > 0 -> Ok Unknown | _ -> From 2048122f114dd24acaa0ff8b4fbd431d92c291f8 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 6 Nov 2024 11:57:42 +0200 Subject: [PATCH 504/566] Fix YAML witness validate/unassume error with empty (unparsable) path Raised an obscure Invalid_argument exception instead. --- src/analyses/unassumeAnalysis.ml | 2 +- src/witness/yamlWitness.ml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 615dbd3266..707e0f4820 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -71,7 +71,7 @@ struct | _ -> () ); - let yaml = match Yaml_unix.of_file (Fpath.v (GobConfig.get_string "witness.yaml.unassume")) with + let yaml = match GobResult.Syntax.(Fpath.of_string (GobConfig.get_string "witness.yaml.unassume") >>= Yaml_unix.of_file) with | Ok yaml -> yaml | Error (`Msg m) -> Logs.error "Yaml_unix.of_file: %s" m; diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 1a8c536da5..06e355068e 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -608,7 +608,7 @@ struct let inv_parser = InvariantParser.create FileCfg.file in - let yaml = match Yaml_unix.of_file (Fpath.v (GobConfig.get_string "witness.yaml.validate")) with + let yaml = match GobResult.Syntax.(Fpath.of_string (GobConfig.get_string "witness.yaml.validate") >>= Yaml_unix.of_file) with | Ok yaml -> yaml | Error (`Msg m) -> Logs.error "Yaml_unix.of_file: %s" m; From 9f7ef77000ef2a86e24c060c767f19ea2840e121 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Wed, 6 Nov 2024 20:13:37 +0200 Subject: [PATCH 505/566] Add backtrace marker around LibraryFunctions special call --- src/autoTune.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/autoTune.ml b/src/autoTune.ml index cae6c62c68..3277291823 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -62,6 +62,7 @@ class findAllocsInLoops = object method! vinst = function | Call (_, Lval (Var f, NoOffset), args,_,_) -> + Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo f) ~finally:Fun.id @@ fun () -> let desc = LibraryFunctions.find f in begin match desc.special args with | Malloc _ From 72bf7d6e7815c27acee1d8b7feedadd8109baff6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 7 Nov 2024 10:14:18 +0200 Subject: [PATCH 506/566] Add is_special check to AutoTune.findAllocsInLoops --- src/autoTune.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autoTune.ml b/src/autoTune.ml index 3277291823..05f651ee62 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -61,7 +61,7 @@ class findAllocsInLoops = object | _ -> DoChildren method! vinst = function - | Call (_, Lval (Var f, NoOffset), args,_,_) -> + | Call (_, Lval (Var f, NoOffset), args,_,_) when LibraryFunctions.is_special f -> Goblint_backtrace.protect ~mark:(fun () -> Cilfacade.FunVarinfo f) ~finally:Fun.id @@ fun () -> let desc = LibraryFunctions.find f in begin match desc.special args with From 065f990a3b35269d36d8b6384deef29d4edb76fa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 8 Nov 2024 15:39:43 +0200 Subject: [PATCH 507/566] Add 27-inv_invariants/22-mine-tutorial-ex4.4 as test --- .../22-mine-tutorial-ex4.4.c | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/regression/27-inv_invariants/22-mine-tutorial-ex4.4.c diff --git a/tests/regression/27-inv_invariants/22-mine-tutorial-ex4.4.c b/tests/regression/27-inv_invariants/22-mine-tutorial-ex4.4.c new file mode 100644 index 0000000000..9770d03de7 --- /dev/null +++ b/tests/regression/27-inv_invariants/22-mine-tutorial-ex4.4.c @@ -0,0 +1,38 @@ +// PARAM: --enable ana.int.interval +#include +int main() { + int x, y, z; + __goblint_assume(0 <= x); + __goblint_assume(x <= 10); + __goblint_assume(5 <= y); + __goblint_assume(y <= 15); + __goblint_assume(-10 <= z); + __goblint_assume(z <= 10); + + if (x >= y) { + __goblint_check(5 <= x); + __goblint_check(y <= 10); // why doesn't Miné refine this? + } + + if (z >= x) { + __goblint_check(0 <= z); + } + + if (x >= y && z >= x) { // CIL transform does branches sequentially (good order) + __goblint_check(5 <= x); + __goblint_check(y <= 10); // why doesn't Miné refine this? + __goblint_check(0 <= z); + + __goblint_check(5 <= z); + } + + if (z >= x && x >= y) { // CIL transform does branches sequentially (bad order) + __goblint_check(5 <= x); + __goblint_check(y <= 10); // why doesn't Miné refine this? + __goblint_check(0 <= z); + + __goblint_check(5 <= z); // TODO + } + + return 0; +} From f2f0c12d0a3ba6946430c467f812b72acc8ad4e0 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Nov 2024 10:48:10 +0200 Subject: [PATCH 508/566] Error on must-relocking of non-recursive mutex mayLocks analysis can warn on it, but mutex analysis can be definite about it. --- src/analyses/mutexAnalysis.ml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 9b6aa4f4ca..a608b3b6e3 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -121,14 +121,17 @@ struct let add ctx ((addr, rw): AddrRW.t): D.t = match addr with - | Addr mv -> + | Addr ((v, o) as mv) -> let (s, m) = ctx.local in let s' = MustLocksetRW.add_mval_rw (mv, rw) s in let m' = - if MutexTypeAnalysis.must_be_recursive ctx mv then - MustMultiplicity.increment mv m - else + match ctx.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) with + | `Lifted Recursive -> MustMultiplicity.increment mv m + | `Lifted NonRec -> + if MustLocksetRW.mem_mval mv s then + M.error ~category:M.Category.Behavior.Undefined.double_locking "Acquiring a non-recursive mutex that is already held"; m + | `Bot | `Top -> m in (s', m') | NullPtr -> From 8d8b6752af3d23260a9c5ab080eb26bdf740006d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Nov 2024 17:40:49 +0200 Subject: [PATCH 509/566] Remove outdated comments about new __VERIFIER_nondet functions --- lib/sv-comp/stub/src/sv-comp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/sv-comp/stub/src/sv-comp.c b/lib/sv-comp/stub/src/sv-comp.c index 12c04125d6..469a641e73 100644 --- a/lib/sv-comp/stub/src/sv-comp.c +++ b/lib/sv-comp/stub/src/sv-comp.c @@ -35,10 +35,10 @@ __VERIFIER_nondet2(unsigned int, u32) __VERIFIER_nondet2(unsigned short int, u16) // not in rules __VERIFIER_nondet2(unsigned char, u8) // not in rules __VERIFIER_nondet2(unsigned char, unsigned_char) // not in rules -__VERIFIER_nondet2(long long, longlong) // not in rules yet (https://gitlab.com/sosy-lab/benchmarking/sv-benchmarks/-/issues/1341) -__VERIFIER_nondet2(unsigned long long, ulonglong) // not in rules yet (https://gitlab.com/sosy-lab/benchmarking/sv-benchmarks/-/issues/1341) -__VERIFIER_nondet2(__uint128_t, uint128) // not in rules yet (https://gitlab.com/sosy-lab/benchmarking/sv-benchmarks/-/issues/1341) -__VERIFIER_nondet2(__int128_t, int128) // not in rules yet (https://gitlab.com/sosy-lab/benchmarking/sv-benchmarks/-/issues/1341) +__VERIFIER_nondet2(long long, longlong) +__VERIFIER_nondet2(unsigned long long, ulonglong) +__VERIFIER_nondet2(__uint128_t, uint128) +__VERIFIER_nondet2(__int128_t, int128) __VERIFIER_nondet2(unsigned char, uchar) __VERIFIER_nondet2(unsigned int, uint) __VERIFIER_nondet2(unsigned long, ulong) From 6a05022657c9da91e90cea46c0c420650a77fb16 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Nov 2024 13:37:44 +0200 Subject: [PATCH 510/566] Add initial CHANGELOG for SV-COMP 2025 --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 420cc7145e..6e9fe29306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## v2.5.0 (unreleased) +Functionally equivalent to Goblint in SV-COMP 2025. + +### SV-COMP 2025 +* Improve invariants (#1361, #1362, #1375, #1328, #1493, #1356). +* Simplify invariants (#1436, #1517). +* Improve YAML witness locations (#1355, #1372, #1400, #1403). +* Improve autotuner (#1469, #1450, #1612, #1604, #1181). +* Loop unrolling (#1582, #1583, #1584, #1516, #1590, #1595, #1599). +* Add abortUnless to svcomp (#1464). +* Fix spurious overflow warnings (#1511). +* Add primitive YAML violation witness rejection (#1301, #1512). +* Machdep support (#54, #1574). + ## v2.4.0 * Remove unmaintained analyses: spec, file (#1281). * Add linear two-variable equalities analysis (#1297, #1412, #1466). From 152ebb633d32275d8cb9924fd54541c2ac64917b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Nov 2024 13:51:22 +0200 Subject: [PATCH 511/566] Add initial CHANGELOG for v2.5.0 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e9fe29306..aec84573cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ## v2.5.0 (unreleased) Functionally equivalent to Goblint in SV-COMP 2025. +* Cleanup (#1095, #1523, #1554, #1575, #1588, #1597, #1614). +* Reduce hash collisions (#1594, #1602). +* Context gas per function (#1569, #1570, #1598). + ### SV-COMP 2025 * Improve invariants (#1361, #1362, #1375, #1328, #1493, #1356). * Simplify invariants (#1436, #1517). From 64981452f455f42cef61de0ab044f3131497db6d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 18 Nov 2024 14:08:10 +0200 Subject: [PATCH 512/566] Add CHANGELOG for v2.5.0 --- CHANGELOG.md | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aec84573cf..cf6a8aa781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,13 @@ ## v2.5.0 (unreleased) Functionally equivalent to Goblint in SV-COMP 2025. -* Cleanup (#1095, #1523, #1554, #1575, #1588, #1597, #1614). -* Reduce hash collisions (#1594, #1602). -* Context gas per function (#1569, #1570, #1598). - -### SV-COMP 2025 -* Improve invariants (#1361, #1362, #1375, #1328, #1493, #1356). -* Simplify invariants (#1436, #1517). -* Improve YAML witness locations (#1355, #1372, #1400, #1403). -* Improve autotuner (#1469, #1450, #1612, #1604, #1181). -* Loop unrolling (#1582, #1583, #1584, #1516, #1590, #1595, #1599). -* Add abortUnless to svcomp (#1464). -* Fix spurious overflow warnings (#1511). -* Add primitive YAML violation witness rejection (#1301, #1512). -* Machdep support (#54, #1574). +* Add 32bit vs 64bit architecture support (#54, #1574). +* Add per-function context gas analysis (#1569, #1570, #1598). +* Adapt automatic static loop unrolling (#1516, #1582, #1583, #1584, #1590, #1595, #1599). +* Adapt automatic configuration tuning (#1450, #1612, #1181, #1604). +* Simplify non-relational integer invariants in witnesses (#1517). +* Fix excessive hash collisions (#1594, #1602). +* Clean up various code (#1095, #1523, #1554, #1575, #1588, #1597, #1614). ## v2.4.0 * Remove unmaintained analyses: spec, file (#1281). @@ -28,7 +21,7 @@ Functionally equivalent to Goblint in SV-COMP 2025. * Fix mutex type analysis unsoundness and enable it by default (#1414, #1416, #1510). * Add points-to set refinement on mutex path splitting (#1287, #1343, #1374, #1396, #1407). * Improve narrowing operators (#1502, #1540, #1543). -* Extract automatic configuration tuning for soundness (#1369). +* Extract automatic configuration tuning for soundness (#1469). * Fix many locations in witnesses (#1355, #1372, #1400, #1403). * Improve output readability (#1294, #1312, #1405, #1497). * Refactor logging (#1117). From ced56ca4882ae5c461d1a7aefd435e814283c02a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 10:06:41 +0200 Subject: [PATCH 513/566] Document YamlEntryGlobal and InvariantGlobalNodes queries --- src/domains/queries.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 5436e5f7e0..e3a0a3e776 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -131,9 +131,9 @@ type _ t = | TmpSpecial: Mval.Exp.t -> ML.t t | MaySignedOverflow: exp -> MayBool.t t | GasExhausted: MustBool.t t - | YamlEntryGlobal: Obj.t * YamlWitnessType.Task.t -> YS.t t + | YamlEntryGlobal: Obj.t * YamlWitnessType.Task.t -> YS.t t (** YAML witness entries for a global unknown ([Obj.t] represents [Spec.V.t]) and YAML witness task. *) | GhostVarAvailable: WitnessGhostVar.t -> MayBool.t t - | InvariantGlobalNodes: NS.t t (* TODO: V.t argument? *) + | InvariantGlobalNodes: NS.t t (** Nodes where YAML witness flow-insensitive invariants should be emitted as location invariants (if [witness.invariant.flow_insensitive-as] is configured to do so). *) (* [Spec.V.t] argument (as [Obj.t]) could be added, if this should be different for different flow-insensitive invariants. *) type 'a result = 'a From 969b87a02e7e956beb8911dc753c42918510a48d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 10:10:38 +0200 Subject: [PATCH 514/566] Replace privatization invariant_global mutex_inits TODO with comment --- src/analyses/apron/relationPriv.apron.ml | 2 +- src/analyses/basePriv.ml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 9b25abb371..abaaa0d9b8 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -722,7 +722,7 @@ struct let one_var = GobConfig.get_bool "ana.relation.invariant.one-var" in let exact = GobConfig.get_bool "witness.invariant.exact" in - let rel = keep_only_protected_globals ask m' (get_m_with_mutex_inits ask getg m') in (* TODO: disjunct with mutex_inits instead of join? *) + let rel = keep_only_protected_globals ask m' (get_m_with_mutex_inits ask getg m') in (* Could be more precise if mutex_inits invariant is added by disjunction instead of joining abstract values. *) let inv = RD.invariant rel |> List.enum diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 347a365778..7a67d0e0fc 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -250,7 +250,7 @@ struct let invariant_global ask getg = function | `Right g' -> (* global *) - ValueDomain.invariant_global (read_unprotected_global getg) g' (* TODO: disjunct with mutex_inits instead of join? *) + ValueDomain.invariant_global (read_unprotected_global getg) g' (* Could be more precise if mutex_inits invariant is added by disjunction instead of joining abstract values. *) | _ -> (* mutex *) Invariant.none @@ -343,7 +343,7 @@ struct | `Left m' -> (* mutex *) let atomic = LockDomain.MustLock.equal m' (LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var) in if atomic || ask.f (GhostVarAvailable (Locked m')) then ( - let cpa = get_m_with_mutex_inits ask getg m' in (* TODO: disjunct with mutex_inits instead of join? *) + let cpa = get_m_with_mutex_inits ask getg m' in (* Could be more precise if mutex_inits invariant is added by disjunction instead of joining abstract values. *) let inv = CPA.fold (fun v _ acc -> if ask.f (MustBeProtectedBy {mutex = m'; global = v; write = true; protection = Strong}) then let inv = ValueDomain.invariant_global (fun g -> CPA.find g cpa) v in @@ -704,7 +704,7 @@ struct let invariant_global ask getg = function | `Middle g -> (* global *) - ValueDomain.invariant_global (read_unprotected_global getg) g (* TODO: disjunct with mutex_inits instead of join? *) + ValueDomain.invariant_global (read_unprotected_global getg) g (* Could be more precise if mutex_inits invariant is added by disjunction instead of joining abstract values. *) | `Left _ | `Right _ -> (* mutex or thread *) Invariant.none From 4940658a23799a16f78731570ca9921ff8f2b0f2 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 10:15:27 +0200 Subject: [PATCH 515/566] Apply suggestions from code review Co-authored-by: Julian Erhard --- src/analyses/mutexGhosts.ml | 1 - src/witness/witnessGhostVar.ml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 09afc41baa..87e7281028 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -10,7 +10,6 @@ struct include UnitAnalysis.Spec let name () = "mutexGhosts" - (* module ThreadCreate = Printable.UnitConf (struct let name = "threadcreate" end) *) module V = struct include Printable.Either3 (Node) (LockDomain.MustLock) (BoolDomain.Bool) diff --git a/src/witness/witnessGhostVar.ml b/src/witness/witnessGhostVar.ml index 82813b4a65..0d71909ba0 100644 --- a/src/witness/witnessGhostVar.ml +++ b/src/witness/witnessGhostVar.ml @@ -35,9 +35,9 @@ include Printable.SimpleShow (struct let describe_varinfo _ _ = "" let typ = function - | Locked _ -> GoblintCil.intType + | Locked _ | Multithreaded -> GoblintCil.intType let initial = function - | Locked _ -> GoblintCil.zero + | Locked _ | Multithreaded -> GoblintCil.zero From 09045bc232f7cf0e36389534eb3fc5e56e1d81d7 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 10:32:17 +0200 Subject: [PATCH 516/566] Add nontrivial condition for querying YamlEntryGlobal at all --- 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 f8890d8eaa..ec83fa04fb 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -397,7 +397,7 @@ struct (* Generate flow-insensitive entries (ghost variables and ghost updates) *) let entries = - if true then ( + if (entry_type_enabled YamlWitnessType.GhostVariable.entry_type && entry_type_enabled YamlWitnessType.GhostUpdate.entry_type) || entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type then ( GHT.fold (fun g v acc -> match g with | `Left g -> (* Spec global *) From 3a07c1615d9a4888a0e972fc6da69719d91e9fb6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 10:47:07 +0200 Subject: [PATCH 517/566] Remove old unnecessary branching from ghost_update YAML witness entries --- src/witness/yamlWitnessType.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 7834951892..fa2d55d2c2 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -464,7 +464,6 @@ struct variable: string; expression: string; location: Location.t; - (* TODO: branching? *) } [@@deriving eq, ord, hash] From 9a3a338287664b9697bf2eaa0eca378bd5be247b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 10:53:46 +0200 Subject: [PATCH 518/566] Implement YamlWitnessType.Entry pretty-printing --- src/witness/yamlWitnessType.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index fa2d55d2c2..6ec133c7d6 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -693,12 +693,6 @@ struct let name () = "YAML entry" - let show _ = "TODO" - include Printable.SimpleShow (struct - type nonrec t = t - let show = show - end) - let to_yaml {entry_type; metadata} = `O ([ ("entry_type", `String (EntryType.entry_type entry_type)); @@ -710,4 +704,10 @@ struct let+ metadata = y |> find "metadata" >>= Metadata.of_yaml and+ entry_type = y |> EntryType.of_yaml in {entry_type; metadata} + + let pp ppf x = Yaml.pp ppf (to_yaml x) + include Printable.SimpleFormat (struct + type nonrec t = t + let pp = pp + end) end From 34277e041b43c093d2d635a586e10686c35a3f8e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 11:24:42 +0200 Subject: [PATCH 519/566] Use sets instead of BatList.mem_cmp for deduplicating ghost witness variables --- src/analyses/mutexGhosts.ml | 19 ++++++------------- src/witness/yamlWitness.ml | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 87e7281028..6542ab3607 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -114,6 +114,8 @@ struct let ghost_var_available ctx v = WitnessGhost.enabled () && ghost_var_available ctx v + module VariableSet = Set.Make (YamlWitnessType.GhostInstrumentation.Variable) + let query ctx (type a) (q: a Queries.t): a Queries.result = match q with | GhostVarAvailable v -> ghost_var_available ctx v @@ -173,15 +175,11 @@ struct let (variables, location_updates) = NodeSet.fold (fun node (variables, location_updates) -> let (locked, unlocked, multithread) = G.node (ctx.global (V.node node)) in let variables' = - (* TODO: do variable_entry-s only once *) Locked.fold (fun l acc -> match mustlock_of_addr l with | Some l when ghost_var_available ctx (Locked l) -> let variable = WitnessGhost.variable' (Locked l) in - if BatList.mem_cmp YamlWitnessType.GhostInstrumentation.Variable.compare variable acc then (* TODO: be efficient *) - acc - else - variable :: acc + VariableSet.add variable acc | _ -> acc ) (Locked.union locked unlocked) variables @@ -211,12 +209,7 @@ struct if ghost_var_available ctx Multithreaded then ( let variable = WitnessGhost.variable' Multithreaded in let update = WitnessGhost.update' Multithreaded GoblintCil.one in - let variables' = - if BatList.mem_cmp YamlWitnessType.GhostInstrumentation.Variable.compare variable variables' then (* TODO: be efficient *) - variables' - else - variable :: variables' - in + let variables' = VariableSet.add variable variables' in (variables', update :: updates) ) else @@ -227,9 +220,9 @@ struct in let location_update = WitnessGhost.location_update' ~node ~updates in (variables', location_update :: location_updates) - ) nodes ([], []) + ) nodes (VariableSet.empty, []) in - let entry = WitnessGhost.instrumentation_entry ~task ~variables ~location_updates in + let entry = WitnessGhost.instrumentation_entry ~task ~variables:(VariableSet.elements variables) ~location_updates in Queries.YS.singleton entry | `Left _ -> Queries.Result.top q | `Middle _ -> Queries.Result.top q diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index ec83fa04fb..a044750a79 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -398,23 +398,24 @@ struct (* Generate flow-insensitive entries (ghost variables and ghost updates) *) let entries = if (entry_type_enabled YamlWitnessType.GhostVariable.entry_type && entry_type_enabled YamlWitnessType.GhostUpdate.entry_type) || entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type then ( - GHT.fold (fun g v acc -> + let module EntrySet = Queries.YS in + fst @@ GHT.fold (fun g v accs -> match g with | `Left g -> (* Spec global *) begin match R.ask_global (YamlEntryGlobal (Obj.repr g, task)) with | `Lifted _ as inv -> - Queries.YS.fold (fun entry acc -> - if BatList.mem_cmp YamlWitnessType.Entry.compare entry acc then (* TODO: be efficient *) - acc + Queries.YS.fold (fun entry (acc, acc') -> + if EntrySet.mem entry acc' then (* deduplicate only with other global entries because local ones have different locations anyway *) + accs else - entry :: acc - ) inv acc + (entry :: acc, EntrySet.add entry acc') + ) inv accs | `Top -> - acc + accs end | `Right _ -> (* contexts global *) - acc - ) gh entries + accs + ) gh (entries, EntrySet.empty ()) ) else entries From 7929d633ee5dc3e30c3607596850bcb64995352f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 11:48:58 +0200 Subject: [PATCH 520/566] Extract fold_flow_insensitive_as_location in YamlWitness to deduplicate code --- src/witness/yamlWitness.ml | 56 ++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index a044750a79..b0ffac5852 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -353,6 +353,22 @@ struct let invariant_global_nodes = lazy (R.ask_global InvariantGlobalNodes) in + let fold_flow_insensitive_as_location ~inv f acc = + (* TODO: or do at location_invariant loop for each node and query if should also do global invariants there? *) + let invs = WitnessUtil.InvariantExp.process_exp inv in + Queries.NS.fold (fun n acc -> + let fundec = Node.find_fundec n in + match WitnessInvariant.location_location n with (* if after thread create node happens to be loop node *) + | Some loc -> + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in + List.fold_left (fun acc inv -> + f ~location ~inv acc + ) acc invs + | None -> acc + ) (Lazy.force invariant_global_nodes) acc + in + (* Generate flow-insensitive invariants *) let entries = if entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type then ( @@ -368,21 +384,11 @@ struct entry :: acc ) acc invs | `Lifted inv, "location_invariant" -> - (* TODO: or do at location_invariant loop for each node and query if should also do global invariants there? *) - let invs = WitnessUtil.InvariantExp.process_exp inv in - Queries.NS.fold (fun n acc -> - let fundec = Node.find_fundec n in - match WitnessInvariant.location_location n with (* if after thread create node happens to be loop node *) - | Some loc -> - let location_function = fundec.svar.vname in - let location = Entry.location ~location:loc ~location_function in - List.fold_left (fun acc inv -> - let invariant = Entry.invariant (CilType.Exp.show inv) in - let entry = Entry.location_invariant ~task ~location ~invariant in - entry :: acc - ) acc invs - | None -> acc - ) (Lazy.force invariant_global_nodes) acc + fold_flow_insensitive_as_location ~inv (fun ~location ~inv acc -> + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.location_invariant ~task ~location ~invariant in + entry :: acc + ) acc | `Lifted _, _ | `Bot, _ | `Top, _ -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) acc @@ -595,21 +601,11 @@ struct | `Left g -> (* Spec global *) begin match R.ask_global (InvariantGlobal (Obj.repr g)) with | `Lifted inv -> - (* TODO: or do at location_invariant loop for each node and query if should also do global invariants there? *) - let invs = WitnessUtil.InvariantExp.process_exp inv in - Queries.NS.fold (fun n acc -> - let fundec = Node.find_fundec n in - match WitnessInvariant.location_location n with (* if after thread create node happens to be loop node *) - | Some loc -> - let location_function = fundec.svar.vname in - let location = Entry.location ~location:loc ~location_function in - List.fold_left (fun acc inv -> - let invariant = CilType.Exp.show inv in - let invariant = Entry.location_invariant' ~location ~invariant in - invariant :: acc - ) acc invs - | None -> acc - ) (Lazy.force invariant_global_nodes) acc + fold_flow_insensitive_as_location ~inv (fun ~location ~inv acc -> + let invariant = CilType.Exp.show inv in + let invariant = Entry.location_invariant' ~location ~invariant in + invariant :: acc + ) acc | `Bot | `Top -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) acc end From 8a0240ddb8753ab6e40691c301550c602fe821cb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 12:08:02 +0200 Subject: [PATCH 521/566] Update ghost witness related TODOs and comments --- src/analyses/apron/relationPriv.apron.ml | 2 +- src/analyses/basePriv.ml | 2 +- src/witness/yamlWitness.ml | 18 ++++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index abaaa0d9b8..02ebe4d0e6 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -747,7 +747,7 @@ struct else Invariant.none | g -> (* global *) - Invariant.none (* TODO: ? *) + Invariant.none (* Could output unprotected one-variable (so non-relational) invariants, but probably not very useful. [BasePriv] does those anyway. *) end (** May written variables. *) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 7a67d0e0fc..c46492b7c7 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -891,7 +891,7 @@ struct else if VD.equal (getg (V.protected g')) (getg (V.unprotected g')) then Invariant.none (* don't output protected invariant because it's the same as unprotected *) else ( - let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) + let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: This takes protected values of every [g], not just [g'], which might be unsound with pointers. See: https://github.com/goblint/analyzer/pull/1394#discussion_r1698136411. *) (* Very conservative about multiple (write-)protecting mutexes: invariant is not claimed when any of them is held. It should be possible to be more precise because writes only happen with all of them held, but conjunction is unsound when one of the mutexes is temporarily unlocked. diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index b0ffac5852..159892fd20 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -354,11 +354,13 @@ struct let invariant_global_nodes = lazy (R.ask_global InvariantGlobalNodes) in let fold_flow_insensitive_as_location ~inv f acc = - (* TODO: or do at location_invariant loop for each node and query if should also do global invariants there? *) + (* Currently same invariants (from InvariantGlobal query) for all nodes (from InvariantGlobalNodes query). + The alternative would be querying InvariantGlobal per local unknown when looping over them to generate location invariants. + See: https://github.com/goblint/analyzer/pull/1394#discussion_r1698149514. *) let invs = WitnessUtil.InvariantExp.process_exp inv in Queries.NS.fold (fun n acc -> let fundec = Node.find_fundec n in - match WitnessInvariant.location_location n with (* if after thread create node happens to be loop node *) + match WitnessInvariant.location_location n with (* Not just using Node.location because it returns expression location which may be invalid for location invariant (e.g. inside conditional). *) | Some loc -> let location_function = fundec.svar.vname in let location = Entry.location ~location:loc ~location_function in @@ -374,7 +376,7 @@ struct if entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type then ( GHT.fold (fun g v acc -> match g with - | `Left g -> (* Spec global *) + | `Left g -> (* global unknown from analysis Spec *) begin match R.ask_global (InvariantGlobal (Obj.repr g)), GobConfig.get_string "witness.invariant.flow_insensitive-as" with | `Lifted inv, "flow_insensitive_invariant" -> let invs = WitnessUtil.InvariantExp.process_exp inv in @@ -393,7 +395,7 @@ struct | `Bot, _ | `Top, _ -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) acc end - | `Right _ -> (* contexts global *) + | `Right _ -> (* global unknown for FromSpec contexts *) acc ) gh entries ) @@ -407,7 +409,7 @@ struct let module EntrySet = Queries.YS in fst @@ GHT.fold (fun g v accs -> match g with - | `Left g -> (* Spec global *) + | `Left g -> (* global unknown from analysis Spec *) begin match R.ask_global (YamlEntryGlobal (Obj.repr g, task)) with | `Lifted _ as inv -> Queries.YS.fold (fun entry (acc, acc') -> @@ -419,7 +421,7 @@ struct | `Top -> accs end - | `Right _ -> (* contexts global *) + | `Right _ -> (* global unknown for FromSpec contexts *) accs ) gh (entries, EntrySet.empty ()) ) @@ -598,7 +600,7 @@ struct if entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type && GobConfig.get_string "witness.invariant.flow_insensitive-as" = "invariant_set-location_invariant" then ( GHT.fold (fun g v acc -> match g with - | `Left g -> (* Spec global *) + | `Left g -> (* global unknown from analysis Spec *) begin match R.ask_global (InvariantGlobal (Obj.repr g)) with | `Lifted inv -> fold_flow_insensitive_as_location ~inv (fun ~location ~inv acc -> @@ -609,7 +611,7 @@ struct | `Bot | `Top -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) acc end - | `Right _ -> (* contexts global *) + | `Right _ -> (* global unknown for FromSpec contexts *) acc ) gh invariants ) From aeb2376811f30d6d9b7f814b685d04643ede5190 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 15:31:39 +0200 Subject: [PATCH 522/566] Clean up Z_mlgmpidl usages --- src/cdomains/apron/apronDomain.apron.ml | 2 +- src/cdomains/apron/gobApron.apron.ml | 2 ++ src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml | 2 +- src/cdomains/apron/sharedFunctions.apron.ml | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index 03ac3ed3f0..043b728799 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -19,7 +19,7 @@ module M = Messages let widening_thresholds_apron = ResettableLazy.from_fun (fun () -> let t = if GobConfig.get_string "ana.apron.threshold_widening_constants" = "comparisons" then WideningThresholds.octagon_thresholds () else WideningThresholds.thresholds_incl_mul2 () in - let r = List.map (fun x -> Apron.Scalar.of_mpqf @@ Mpqf.of_mpz @@ Z_mlgmpidl.mpz_of_z x) t in + let r = List.map Scalar.of_z t in Array.of_list r ) diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml index fbb1fe9ec5..327e43e321 100644 --- a/src/cdomains/apron/gobApron.apron.ml +++ b/src/cdomains/apron/gobApron.apron.ml @@ -12,6 +12,8 @@ struct let pp = pp end ) + + let of_z z = of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z z)) end module Coeff = diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index c1ca3661a5..6af7030a51 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -790,7 +790,7 @@ struct let of_coeff xi coeffs o = let typ = (Option.get @@ V.to_cil_varinfo xi).vtype in let ikind = Cilfacade.get_ikind typ in - let cst = Coeff.s_of_mpqf @@ Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z @@ IntDomain.Size.cast ikind o) in + let cst = Coeff.s_of_z (IntDomain.Size.cast ikind o) in let lincons = Lincons1.make (Linexpr1.make t.env) Lincons1.EQ in Lincons1.set_list lincons coeffs (Some cst); lincons diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 86b5f2770f..b9d93bfd99 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -133,7 +133,7 @@ struct else failwith "texpr1_expr_of_cil_exp: globals must be replaced with temporary locals" | Const (CInt (i, _, _)) -> - Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z i))) + Cst (Coeff.s_of_z i) | exp -> match Cilfacade.get_ikind_exp exp with | ik -> @@ -175,7 +175,7 @@ struct (* convert response to a constant *) let const = IntDomain.IntDomTuple.to_int @@ IntDomain.IntDomTuple.cast_to t_ik res in match const with - | Some c -> Cst (Coeff.s_of_mpqf (Mpqf.of_mpz (Z_mlgmpidl.mpz_of_z c))) (* Got a constant value -> use it straight away *) + | Some c -> Cst (Coeff.s_of_z c) (* Got a constant value -> use it straight away *) (* I gotten top, we can not guarantee injectivity *) | None -> if IntDomain.IntDomTuple.is_top_of t_ik res then raise (Unsupported_CilExp (Cast_not_injective t)) else ( (* Got a ranged value different from top, so let's check bounds manually *) From 64c11c2748d2981346d909392423fde938c8ca8c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 16:10:36 +0200 Subject: [PATCH 523/566] Add test 56-witness/69-ghost-ptr-protection for unsound protection flow-sensitive invariant --- .../56-witness/69-ghost-ptr-protection.c | 30 ++++++ .../56-witness/69-ghost-ptr-protection.t | 101 ++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 tests/regression/56-witness/69-ghost-ptr-protection.c create mode 100644 tests/regression/56-witness/69-ghost-ptr-protection.t diff --git a/tests/regression/56-witness/69-ghost-ptr-protection.c b/tests/regression/56-witness/69-ghost-ptr-protection.c new file mode 100644 index 0000000000..f5557f3fc8 --- /dev/null +++ b/tests/regression/56-witness/69-ghost-ptr-protection.c @@ -0,0 +1,30 @@ +// PARAM: --set ana.activated[+] mutexGhosts +// CRAM +#include +#include + +int g = 0; +int *p = &g; +pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + int x = 10; + pthread_mutex_lock(&m2); + p = &x; + p = &g; + pthread_mutex_unlock(&m2); + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&m1); + g = 1; + // m2_locked || (p == & g && *p == 0) would be violated here + __goblint_check(*p != 0); // 1 from g or 10 from x in t_fun + g = 0; + pthread_mutex_unlock(&m1); + return 0; +} diff --git a/tests/regression/56-witness/69-ghost-ptr-protection.t b/tests/regression/56-witness/69-ghost-ptr-protection.t new file mode 100644 index 0000000000..03481a7ce1 --- /dev/null +++ b/tests/regression/56-witness/69-ghost-ptr-protection.t @@ -0,0 +1,101 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 69-ghost-ptr-protection.c + [Success][Assert] Assertion "*p != 0" will succeed (69-ghost-ptr-protection.c:26:3-26:27) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 15 + dead: 0 + total lines: 15 + [Warning][Race] Memory location p (race with conf. 110): (69-ghost-ptr-protection.c:7:5-7:12) + write with [lock:{m2}, thread:[main, t_fun@69-ghost-ptr-protection.c:22:3-22:40]] (conf. 110) (exp: & p) (69-ghost-ptr-protection.c:14:3-14:9) + write with [lock:{m2}, thread:[main, t_fun@69-ghost-ptr-protection.c:22:3-22:40]] (conf. 110) (exp: & p) (69-ghost-ptr-protection.c:15:3-15:9) + read with [mhp:{created={[main, t_fun@69-ghost-ptr-protection.c:22:3-22:40]}}, lock:{m1}, thread:[main]] (conf. 110) (exp: & p) (69-ghost-ptr-protection.c:26:3-26:27) + [Info][Witness] witness generation summary: + total generation entries: 12 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 1 + total memory locations: 3 + +TODO: Should not contain unsound flow-insensitive invariant m2_locked || (p == & g && *p == 0): + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_update + variable: multithreaded + expression: "1" + location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: main + - entry_type: ghost_update + variable: m2_locked + expression: "1" + location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: m2_locked + expression: "0" + location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 16 + column: 3 + function: t_fun + - entry_type: ghost_update + variable: m1_locked + expression: "1" + location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + - entry_type: ghost_update + variable: m1_locked + expression: "0" + location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + - entry_type: ghost_variable + variable: multithreaded + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m2_locked + scope: global + type: int + initial: "0" + - entry_type: ghost_variable + variable: m1_locked + scope: global + type: int + initial: "0" + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || (p == & g && *p == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m1_locked || g == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g && g <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (*p == 10 || ((0 <= *p && *p <= 1) && p == & g))' + type: assertion + format: C From b4734c31753b328b74283f9f82351fd6e09979c9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 16:22:33 +0200 Subject: [PATCH 524/566] Fix unsound ghost witness invariant in 56-witness/69-ghost-ptr-protection --- src/analyses/basePriv.ml | 5 ++++- tests/regression/56-witness/69-ghost-ptr-protection.t | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index b7e32ceb94..3afd758daa 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -891,7 +891,10 @@ struct else if VD.equal (getg (V.protected g')) (getg (V.unprotected g')) then Invariant.none (* don't output protected invariant because it's the same as unprotected *) else ( - let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: This takes protected values of every [g], not just [g'], which might be unsound with pointers. See: https://github.com/goblint/analyzer/pull/1394#discussion_r1698136411. *) + (* Only read g' as protected, everything else (e.g. pointed to variables) may be unprotected. + See 56-witness/69-ghost-ptr-protection and https://github.com/goblint/analyzer/pull/1394#discussion_r1698136411. *) + let read_global g = getg (if CilType.Varinfo.equal g g' then V.protected g else V.unprotected g) in + let inv = ValueDomain.invariant_global read_global g' in (* Very conservative about multiple (write-)protecting mutexes: invariant is not claimed when any of them is held. It should be possible to be more precise because writes only happen with all of them held, but conjunction is unsound when one of the mutexes is temporarily unlocked. diff --git a/tests/regression/56-witness/69-ghost-ptr-protection.t b/tests/regression/56-witness/69-ghost-ptr-protection.t index 03481a7ce1..698f643385 100644 --- a/tests/regression/56-witness/69-ghost-ptr-protection.t +++ b/tests/regression/56-witness/69-ghost-ptr-protection.t @@ -16,7 +16,7 @@ unsafe: 1 total memory locations: 3 -TODO: Should not contain unsound flow-insensitive invariant m2_locked || (p == & g && *p == 0): +Should not contain unsound flow-insensitive invariant m2_locked || (p == & g && *p == 0): $ yamlWitnessStrip < witness.yml - entry_type: ghost_update @@ -81,7 +81,7 @@ TODO: Should not contain unsound flow-insensitive invariant m2_locked || (p == & initial: "0" - entry_type: flow_insensitive_invariant flow_insensitive_invariant: - string: '! multithreaded || (m2_locked || (p == & g && *p == 0))' + string: '! multithreaded || (m2_locked || ((0 <= *p && *p <= 1) && p == & g))' type: assertion format: C - entry_type: flow_insensitive_invariant From d2e71cbbf773205abb600fc15cf07ba712a2e6eb Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 16:58:43 +0200 Subject: [PATCH 525/566] Change ghost witness tests to use ghost_instrumentation --- .../regression/13-privatized/04-priv_multi.t | 298 +++---- tests/regression/13-privatized/25-struct_nr.t | 125 +-- tests/regression/13-privatized/74-mutex.t | 258 +++--- tests/regression/13-privatized/92-idx_priv.t | 66 +- tests/regression/29-svcomp/16-atomic_priv.t | 76 +- .../regression/36-apron/12-traces-min-rpb1.t | 163 ++-- .../56-witness/64-ghost-multiple-protecting.t | 750 ++++++++++-------- .../56-witness/65-ghost-ambiguous-lock.t | 94 ++- .../56-witness/66-ghost-alloc-lock.t | 212 ++--- .../56-witness/67-ghost-no-unlock.t | 106 +-- .../56-witness/68-ghost-ambiguous-idx.t | 66 +- .../56-witness/69-ghost-ptr-protection.t | 136 ++-- 12 files changed, 1325 insertions(+), 1025 deletions(-) diff --git a/tests/regression/13-privatized/04-priv_multi.t b/tests/regression/13-privatized/04-priv_multi.t index fd0dad6a39..3ea9b385fc 100644 --- a/tests/regression/13-privatized/04-priv_multi.t +++ b/tests/regression/13-privatized/04-priv_multi.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 04-priv_multi.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 04-priv_multi.c [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) [Warning][Deadcode] Function 'dispose' has dead code: @@ -16,7 +16,7 @@ [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:45:10-45:11) [Warning][Deadcode][CWE-571] condition 'B > 0' is always true (04-priv_multi.c:47:9-47:14) [Info][Witness] witness generation summary: - total generation entries: 19 + total generation entries: 4 [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 @@ -24,138 +24,158 @@ total memory locations: 2 $ yamlWitnessStrip < witness.yml | tee witness.flow_insensitive.yml - - entry_type: ghost_update - variable: mutex_B_locked - expression: "1" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 69 - column: 5 - function: main - - entry_type: ghost_update - variable: mutex_B_locked - expression: "1" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 46 - column: 5 - function: dispose - - entry_type: ghost_update - variable: mutex_B_locked - expression: "1" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 29 - column: 7 - function: process - - entry_type: ghost_update - variable: mutex_B_locked - expression: "0" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 73 - column: 5 - function: main - - entry_type: ghost_update - variable: mutex_B_locked - expression: "0" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 49 - column: 7 - function: dispose - - entry_type: ghost_update - variable: mutex_B_locked - expression: "0" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 32 - column: 7 - function: process - - entry_type: ghost_update - variable: mutex_A_locked - expression: "1" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 68 - column: 5 - function: main - - entry_type: ghost_update - variable: mutex_A_locked - expression: "1" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 26 - column: 5 - function: process - - entry_type: ghost_update - variable: mutex_A_locked - expression: "1" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 15 - column: 5 - function: generate - - entry_type: ghost_update - variable: mutex_A_locked - expression: "0" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 74 - column: 5 - function: main - - entry_type: ghost_update - variable: mutex_A_locked - expression: "0" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 34 - column: 7 - function: process - - entry_type: ghost_update - variable: mutex_A_locked - expression: "0" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 18 - column: 5 - function: generate - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 04-priv_multi.c - file_hash: $FILE_HASH - line: 63 - column: 3 - function: main - - entry_type: ghost_variable - variable: mutex_B_locked - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: mutex_A_locked - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: mutex_A_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: mutex_B_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 15 + column: 5 + function: generate + updates: + - variable: mutex_A_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 18 + column: 5 + function: generate + updates: + - variable: mutex_A_locked + value: "0" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 26 + column: 5 + function: process + updates: + - variable: mutex_A_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 29 + column: 7 + function: process + updates: + - variable: mutex_B_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 32 + column: 7 + function: process + updates: + - variable: mutex_B_locked + value: "0" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 34 + column: 7 + function: process + updates: + - variable: mutex_A_locked + value: "0" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 46 + column: 5 + function: dispose + updates: + - variable: mutex_B_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 49 + column: 7 + function: dispose + updates: + - variable: mutex_B_locked + value: "0" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 63 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 68 + column: 5 + function: main + updates: + - variable: mutex_A_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 69 + column: 5 + function: main + updates: + - variable: mutex_B_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 73 + column: 5 + function: main + updates: + - variable: mutex_B_locked + value: "0" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 74 + column: 5 + function: main + updates: + - variable: mutex_A_locked + value: "0" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (mutex_B_locked || (mutex_A_locked || B == 5))' @@ -174,7 +194,7 @@ Flow-insensitive invariants as location invariants. - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --set witness.invariant.flow_insensitive-as location_invariant 04-priv_multi.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' --set witness.invariant.flow_insensitive-as location_invariant 04-priv_multi.c [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) [Warning][Deadcode] Function 'dispose' has dead code: @@ -192,7 +212,7 @@ Flow-insensitive invariants as location invariants. [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:45:10-45:11) [Warning][Deadcode][CWE-571] condition 'B > 0' is always true (04-priv_multi.c:47:9-47:14) [Info][Witness] witness generation summary: - total generation entries: 25 + total generation entries: 10 [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 @@ -204,7 +224,7 @@ Flow-insensitive invariants as location invariants. Location invariant at `for` loop in `main` should be on column 3, not 7. $ diff witness.flow_insensitive.yml witness.location.yml - 133,134c133,140 + 153,154c153,160 < - entry_type: flow_insensitive_invariant < flow_insensitive_invariant: --- @@ -216,7 +236,7 @@ Location invariant at `for` loop in `main` should be on column 3, not 7. > column: 3 > function: main > location_invariant: - 138,139c144,151 + 158,159c164,171 < - entry_type: flow_insensitive_invariant < flow_insensitive_invariant: --- @@ -228,7 +248,7 @@ Location invariant at `for` loop in `main` should be on column 3, not 7. > column: 3 > function: main > location_invariant: - 143,144c155,228 + 163,164c175,248 < - entry_type: flow_insensitive_invariant < flow_insensitive_invariant: --- diff --git a/tests/regression/13-privatized/25-struct_nr.t b/tests/regression/13-privatized/25-struct_nr.t index 88f205a431..59ed9960f2 100644 --- a/tests/regression/13-privatized/25-struct_nr.t +++ b/tests/regression/13-privatized/25-struct_nr.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 25-struct_nr.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 25-struct_nr.c [Success][Assert] Assertion "glob1 == 5" will succeed (25-struct_nr.c:26:3-26:30) [Success][Assert] Assertion "t == 5" will succeed (25-struct_nr.c:16:3-16:26) [Success][Assert] Assertion "glob1 == -10" will succeed (25-struct_nr.c:18:3-18:32) @@ -8,7 +8,7 @@ dead: 0 total lines: 19 [Info][Witness] witness generation summary: - total generation entries: 9 + total generation entries: 3 [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 @@ -16,61 +16,72 @@ total memory locations: 1 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 25-struct_nr.c - file_hash: $FILE_HASH - line: 27 - column: 3 - function: main - - entry_type: ghost_update - variable: lock1_mutex_locked - expression: "1" - location: - file_name: 25-struct_nr.c - file_hash: $FILE_HASH - line: 28 - column: 3 - function: main - - entry_type: ghost_update - variable: lock1_mutex_locked - expression: "1" - location: - file_name: 25-struct_nr.c - file_hash: $FILE_HASH - line: 14 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: lock1_mutex_locked - expression: "0" - location: - file_name: 25-struct_nr.c - file_hash: $FILE_HASH - line: 32 - column: 3 - function: main - - entry_type: ghost_update - variable: lock1_mutex_locked - expression: "0" - location: - file_name: 25-struct_nr.c - file_hash: $FILE_HASH - line: 20 - column: 3 - function: t_fun - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: lock1_mutex_locked - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: lock1_mutex_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: + - variable: lock1_mutex_locked + value: "1" + format: c_expression + - location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: t_fun + updates: + - variable: lock1_mutex_locked + value: "0" + format: c_expression + - location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 27 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + updates: + - variable: lock1_mutex_locked + value: "1" + format: c_expression + - location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 32 + column: 3 + function: main + updates: + - variable: lock1_mutex_locked + value: "0" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (lock1_mutex_locked || glob1 == 5)' diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index 8a1a7fee5f..1c1f0c12be 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -1,4 +1,4 @@ - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) @@ -8,7 +8,7 @@ total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: - total generation entries: 9 + total generation entries: 3 [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 @@ -16,61 +16,72 @@ total memory locations: 1 $ yamlWitnessStrip < witness.yml | tee witness.flow_insensitive.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 34 - column: 3 - function: main - - entry_type: ghost_update - variable: m_locked - expression: "1" - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 36 - column: 3 - function: main - - entry_type: ghost_update - variable: m_locked - expression: "1" - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 20 - column: 5 - function: producer - - entry_type: ghost_update - variable: m_locked - expression: "0" - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 38 - column: 3 - function: main - - entry_type: ghost_update - variable: m_locked - expression: "0" - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 23 - column: 5 - function: producer - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m_locked - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 20 + column: 5 + function: producer + updates: + - variable: m_locked + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 23 + column: 5 + function: producer + updates: + - variable: m_locked + value: "0" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 34 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + updates: + - variable: m_locked + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 38 + column: 3 + function: main + updates: + - variable: m_locked + value: "0" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (m_locked || used == 0)' @@ -84,7 +95,7 @@ Flow-insensitive invariants as location invariants. - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' --set witness.invariant.flow_insensitive-as location_invariant 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' --set witness.invariant.flow_insensitive-as location_invariant 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) @@ -94,7 +105,7 @@ Flow-insensitive invariants as location invariants. total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: - total generation entries: 9 + total generation entries: 3 [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 @@ -104,7 +115,7 @@ Flow-insensitive invariants as location invariants. $ yamlWitnessStrip < witness.yml > witness.location.yml $ diff witness.flow_insensitive.yml witness.location.yml - 56,57c56,63 + 67,68c67,74 < - entry_type: flow_insensitive_invariant < flow_insensitive_invariant: --- @@ -116,7 +127,7 @@ Flow-insensitive invariants as location invariants. > column: 3 > function: main > location_invariant: - 61,62c67,74 + 72,73c78,85 < - entry_type: flow_insensitive_invariant < flow_insensitive_invariant: --- @@ -259,7 +270,7 @@ Same with ghost_instrumentation and invariant_set entries. Same with mutex-meet. - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 74-mutex.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 74-mutex.c [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) [Warning][Deadcode] Function 'producer' has dead code: on line 26 (74-mutex.c:26-26) @@ -269,7 +280,7 @@ Same with mutex-meet. total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: - total generation entries: 9 + total generation entries: 3 [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 @@ -277,61 +288,72 @@ Same with mutex-meet. total memory locations: 1 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 34 - column: 3 - function: main - - entry_type: ghost_update - variable: m_locked - expression: "1" - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 36 - column: 3 - function: main - - entry_type: ghost_update - variable: m_locked - expression: "1" - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 20 - column: 5 - function: producer - - entry_type: ghost_update - variable: m_locked - expression: "0" - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 38 - column: 3 - function: main - - entry_type: ghost_update - variable: m_locked - expression: "0" - location: - file_name: 74-mutex.c - file_hash: $FILE_HASH - line: 23 - column: 5 - function: producer - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m_locked - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 20 + column: 5 + function: producer + updates: + - variable: m_locked + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 23 + column: 5 + function: producer + updates: + - variable: m_locked + value: "0" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 34 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + updates: + - variable: m_locked + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 38 + column: 3 + function: main + updates: + - variable: m_locked + value: "0" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (m_locked || used == 0)' diff --git a/tests/regression/13-privatized/92-idx_priv.t b/tests/regression/13-privatized/92-idx_priv.t index b157dfed4b..4783f65092 100644 --- a/tests/regression/13-privatized/92-idx_priv.t +++ b/tests/regression/13-privatized/92-idx_priv.t @@ -1,11 +1,11 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 92-idx_priv.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 92-idx_priv.c [Success][Assert] Assertion "data == 0" will succeed (92-idx_priv.c:22:3-22:29) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 14 dead: 0 total lines: 14 [Info][Witness] witness generation summary: - total generation entries: 3 + total generation entries: 2 [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 @@ -13,20 +13,54 @@ total memory locations: 1 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 92-idx_priv.c - file_hash: $FILE_HASH - line: 20 - column: 3 - function: main - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 92-idx_priv.c + file_hash: $FILE_HASH + line: 8 + column: 3 + function: t_fun + updates: [] + - location: + file_name: 92-idx_priv.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + updates: [] + - location: + file_name: 92-idx_priv.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 92-idx_priv.c + file_hash: $FILE_HASH + line: 21 + column: 3 + function: main + updates: [] + - location: + file_name: 92-idx_priv.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + updates: [] - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (0 <= data && data <= 1)' diff --git a/tests/regression/29-svcomp/16-atomic_priv.t b/tests/regression/29-svcomp/16-atomic_priv.t index eea47295d5..92afedcd27 100644 --- a/tests/regression/29-svcomp/16-atomic_priv.t +++ b/tests/regression/29-svcomp/16-atomic_priv.t @@ -1,4 +1,4 @@ - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection-atomic --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 16-atomic_priv.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection-atomic --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 16-atomic_priv.c [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:12:3-12:33) [Success][Assert] Assertion "myglobal == 6" will succeed (16-atomic_priv.c:14:3-14:33) [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:16:3-16:33) @@ -13,7 +13,7 @@ write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:15:3-15:13) read with [mhp:{created={[main, t_fun@16-atomic_priv.c:23:3-23:40]}}, thread:[main]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:24:3-24:33) [Info][Witness] witness generation summary: - total generation entries: 3 + total generation entries: 2 [Info][Race] Memory locations race summary: safe: 0 vulnerable: 0 @@ -21,20 +21,26 @@ total memory locations: 1 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 16-atomic_priv.c - file_hash: $FILE_HASH - line: 23 - column: 3 - function: main - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || myglobal == 5' @@ -43,7 +49,7 @@ Non-atomic privatization: - $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 16-atomic_priv.c + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 16-atomic_priv.c [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:12:3-12:33) [Success][Assert] Assertion "myglobal == 6" will succeed (16-atomic_priv.c:14:3-14:33) [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:16:3-16:33) @@ -58,7 +64,7 @@ Non-atomic privatization: write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:15:3-15:13) read with [mhp:{created={[main, t_fun@16-atomic_priv.c:23:3-23:40]}}, thread:[main]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:24:3-24:33) [Info][Witness] witness generation summary: - total generation entries: 4 + total generation entries: 3 [Info][Race] Memory locations race summary: safe: 0 vulnerable: 0 @@ -66,20 +72,26 @@ Non-atomic privatization: total memory locations: 1 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 16-atomic_priv.c - file_hash: $FILE_HASH - line: 23 - column: 3 - function: main - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || myglobal == 5' diff --git a/tests/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t index 8201b2f8f9..c0ae5c118e 100644 --- a/tests/regression/36-apron/12-traces-min-rpb1.t +++ b/tests/regression/36-apron/12-traces-min-rpb1.t @@ -56,7 +56,7 @@ format: C - $ goblint --enable warn.deterministic --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 12-traces-min-rpb1.c --enable ana.apron.invariant.diff-box + $ goblint --enable warn.deterministic --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 12-traces-min-rpb1.c --enable ana.apron.invariant.diff-box [Warning][Assert] Assertion "g == h" is unknown. (12-traces-min-rpb1.c:27:3-27:26) [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:16:3-16:26) [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:29:3-29:26) @@ -76,82 +76,95 @@ dead: 0 total lines: 18 [Info][Witness] witness generation summary: - total generation entries: 10 + total generation entries: 2 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 12-traces-min-rpb1.c - file_hash: $FILE_HASH - line: 25 - column: 3 - function: main - - entry_type: ghost_update - variable: A_locked - expression: "1" - location: - file_name: 12-traces-min-rpb1.c - file_hash: $FILE_HASH - line: 28 - column: 3 - function: main - - entry_type: ghost_update - variable: A_locked - expression: "1" - location: - file_name: 12-traces-min-rpb1.c - file_hash: $FILE_HASH - line: 18 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: A_locked - expression: "1" - location: - file_name: 12-traces-min-rpb1.c - file_hash: $FILE_HASH - line: 13 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: A_locked - expression: "0" - location: - file_name: 12-traces-min-rpb1.c - file_hash: $FILE_HASH - line: 30 - column: 3 - function: main - - entry_type: ghost_update - variable: A_locked - expression: "0" - location: - file_name: 12-traces-min-rpb1.c - file_hash: $FILE_HASH - line: 19 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: A_locked - expression: "0" - location: - file_name: 12-traces-min-rpb1.c - file_hash: $FILE_HASH - line: 17 - column: 3 - function: t_fun - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: A_locked - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: A_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: A_locked + value: "1" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: + - variable: A_locked + value: "0" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 18 + column: 3 + function: t_fun + updates: + - variable: A_locked + value: "1" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 19 + column: 3 + function: t_fun + updates: + - variable: A_locked + value: "0" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 25 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + updates: + - variable: A_locked + value: "1" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 30 + column: 3 + function: main + updates: + - variable: A_locked + value: "0" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (A_locked || ((0LL - (long long )g) + (long long )h diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.t b/tests/regression/56-witness/64-ghost-multiple-protecting.t index e78d0d75aa..73863eecac 100644 --- a/tests/regression/56-witness/64-ghost-multiple-protecting.t +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.t @@ -1,10 +1,10 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 total lines: 19 [Info][Witness] witness generation summary: - total generation entries: 17 + total generation entries: 4 [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 @@ -14,120 +14,138 @@ protection doesn't have precise protected invariant for g2. $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 29 - column: 3 - function: main - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 20 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 17 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 10 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 22 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 19 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 13 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 16 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 9 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 23 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 14 - column: 3 - function: t_fun - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m2_locked - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m1_locked - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m1_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: m2_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 9 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 16 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 19 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 29 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (m2_locked || (m1_locked || g1 == 0))' @@ -144,13 +162,13 @@ protection doesn't have precise protected invariant for g2. type: assertion format: C - $ goblint --set ana.base.privatization protection-read --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization protection-read --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 total lines: 19 [Info][Witness] witness generation summary: - total generation entries: 18 + total generation entries: 5 [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 @@ -160,120 +178,138 @@ protection doesn't have precise protected invariant for g2. protection-read has precise protected invariant for g2. $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 29 - column: 3 - function: main - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 20 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 17 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 10 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 22 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 19 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 13 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 16 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 9 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 23 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 14 - column: 3 - function: t_fun - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m2_locked - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m1_locked - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m1_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: m2_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 9 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 16 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 19 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 29 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (m2_locked || (m1_locked || g2 == 0))' @@ -295,13 +331,13 @@ protection-read has precise protected invariant for g2. type: assertion format: C - $ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 64-ghost-multiple-protecting.c + $ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 64-ghost-multiple-protecting.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 19 dead: 0 total lines: 19 [Info][Witness] witness generation summary: - total generation entries: 18 + total generation entries: 5 [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 @@ -309,120 +345,138 @@ protection-read has precise protected invariant for g2. total memory locations: 2 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 29 - column: 3 - function: main - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 20 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 17 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 10 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 22 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 19 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 13 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 16 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 9 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 23 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 64-ghost-multiple-protecting.c - file_hash: $FILE_HASH - line: 14 - column: 3 - function: t_fun - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m2_locked - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m1_locked - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m1_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: m2_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 9 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 16 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 19 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 29 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (m2_locked || ((0 <= g2 && g2 <= 1) && g1 == 0))' diff --git a/tests/regression/56-witness/65-ghost-ambiguous-lock.t b/tests/regression/56-witness/65-ghost-ambiguous-lock.t index a6e0c12b74..8115bb2921 100644 --- a/tests/regression/56-witness/65-ghost-ambiguous-lock.t +++ b/tests/regression/56-witness/65-ghost-ambiguous-lock.t @@ -1,10 +1,10 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 65-ghost-ambiguous-lock.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 65-ghost-ambiguous-lock.c [Info][Deadcode] Logical lines of code (LLoC) summary: live: 23 dead: 0 total lines: 23 [Info][Witness] witness generation summary: - total generation entries: 4 + total generation entries: 3 [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 @@ -12,20 +12,82 @@ total memory locations: 2 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 29 - column: 3 - function: main - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + updates: [] + - location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: [] + - location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: [] + - location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: [] + - location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: fun + updates: [] + - location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 24 + column: 3 + function: fun + updates: [] + - location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 29 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 35 + column: 3 + function: main + updates: [] + - location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 37 + column: 3 + function: main + updates: [] - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (0 <= g2 && g2 <= 1)' diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.t b/tests/regression/56-witness/66-ghost-alloc-lock.t index 8e45272538..844c1e6c15 100644 --- a/tests/regression/56-witness/66-ghost-alloc-lock.t +++ b/tests/regression/56-witness/66-ghost-alloc-lock.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.malloc.unique_address_count 1 --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 66-ghost-alloc-lock.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.malloc.unique_address_count 1 --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 66-ghost-alloc-lock.c [Success][Assert] Assertion "g1 == 0" will succeed (66-ghost-alloc-lock.c:31:3-31:27) [Success][Assert] Assertion "g2 == 0" will succeed (66-ghost-alloc-lock.c:34:3-34:27) [Info][Deadcode] Logical lines of code (LLoC) summary: @@ -6,7 +6,7 @@ dead: 0 total lines: 23 [Info][Witness] witness generation summary: - total generation entries: 16 + total generation entries: 5 [Info][Race] Memory locations race summary: safe: 4 vulnerable: 0 @@ -14,102 +14,118 @@ total memory locations: 4 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 66-ghost-alloc-lock.c - file_hash: $FILE_HASH - line: 28 - column: 3 - function: main - - entry_type: ghost_update - variable: alloc_m861095507_locked - expression: "1" - location: - file_name: 66-ghost-alloc-lock.c - file_hash: $FILE_HASH - line: 33 - column: 3 - function: main - - entry_type: ghost_update - variable: alloc_m861095507_locked - expression: "1" - location: - file_name: 66-ghost-alloc-lock.c - file_hash: $FILE_HASH - line: 14 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: alloc_m861095507_locked - expression: "0" - location: - file_name: 66-ghost-alloc-lock.c - file_hash: $FILE_HASH - line: 35 - column: 3 - function: main - - entry_type: ghost_update - variable: alloc_m861095507_locked - expression: "0" - location: - file_name: 66-ghost-alloc-lock.c - file_hash: $FILE_HASH - line: 17 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: alloc_m559918035_locked - expression: "1" - location: - file_name: 66-ghost-alloc-lock.c - file_hash: $FILE_HASH - line: 30 - column: 3 - function: main - - entry_type: ghost_update - variable: alloc_m559918035_locked - expression: "1" - location: - file_name: 66-ghost-alloc-lock.c - file_hash: $FILE_HASH - line: 10 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: alloc_m559918035_locked - expression: "0" - location: - file_name: 66-ghost-alloc-lock.c - file_hash: $FILE_HASH - line: 32 - column: 3 - function: main - - entry_type: ghost_update - variable: alloc_m559918035_locked - expression: "0" - location: - file_name: 66-ghost-alloc-lock.c - file_hash: $FILE_HASH - line: 13 - column: 3 - function: t_fun - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: alloc_m861095507_locked - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: alloc_m559918035_locked - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: alloc_m559918035_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: alloc_m861095507_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + updates: + - variable: alloc_m559918035_locked + value: "1" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: alloc_m559918035_locked + value: "0" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: + - variable: alloc_m861095507_locked + value: "1" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: + - variable: alloc_m861095507_locked + value: "0" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 30 + column: 3 + function: main + updates: + - variable: alloc_m559918035_locked + value: "1" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 32 + column: 3 + function: main + updates: + - variable: alloc_m559918035_locked + value: "0" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 33 + column: 3 + function: main + updates: + - variable: alloc_m861095507_locked + value: "1" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 35 + column: 3 + function: main + updates: + - variable: alloc_m861095507_locked + value: "0" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (alloc_m861095507_locked || g2 == 0)' diff --git a/tests/regression/56-witness/67-ghost-no-unlock.t b/tests/regression/56-witness/67-ghost-no-unlock.t index 85b7a0b897..264d592366 100644 --- a/tests/regression/56-witness/67-ghost-no-unlock.t +++ b/tests/regression/56-witness/67-ghost-no-unlock.t @@ -1,11 +1,11 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 67-ghost-no-unlock.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 67-ghost-no-unlock.c [Success][Assert] Assertion "g1 == 0" will succeed (67-ghost-no-unlock.c:24:3-24:27) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 11 dead: 0 total lines: 11 [Info][Witness] witness generation summary: - total generation entries: 8 + total generation entries: 3 [Info][Race] Memory locations race summary: safe: 1 vulnerable: 0 @@ -13,52 +13,62 @@ total memory locations: 1 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 67-ghost-no-unlock.c - file_hash: $FILE_HASH - line: 21 - column: 3 - function: main - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 67-ghost-no-unlock.c - file_hash: $FILE_HASH - line: 23 - column: 3 - function: main - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 67-ghost-no-unlock.c - file_hash: $FILE_HASH - line: 9 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 67-ghost-no-unlock.c - file_hash: $FILE_HASH - line: 12 - column: 3 - function: t_fun - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m1_locked - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m1_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 9 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 12 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 21 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + updates: + - variable: m1_locked + value: "1" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (m1_locked || g1 == 0)' diff --git a/tests/regression/56-witness/68-ghost-ambiguous-idx.t b/tests/regression/56-witness/68-ghost-ambiguous-idx.t index 02cecfd8f6..a8f2a0226a 100644 --- a/tests/regression/56-witness/68-ghost-ambiguous-idx.t +++ b/tests/regression/56-witness/68-ghost-ambiguous-idx.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 68-ghost-ambiguous-idx.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 68-ghost-ambiguous-idx.c [Warning][Assert] Assertion "data == 0" is unknown. (68-ghost-ambiguous-idx.c:24:3-24:29) [Warning][Unknown] unlocking mutex (m[4]) which may not be held (68-ghost-ambiguous-idx.c:25:3-25:30) [Info][Deadcode] Logical lines of code (LLoC) summary: @@ -10,7 +10,7 @@ write with [lock:{m[4]}, thread:[main, t_fun@68-ghost-ambiguous-idx.c:20:3-20:40]] (conf. 110) (exp: & data) (68-ghost-ambiguous-idx.c:10:3-10:9) read with [mhp:{created={[main, t_fun@68-ghost-ambiguous-idx.c:20:3-20:40]}}, thread:[main]] (conf. 110) (exp: & data) (68-ghost-ambiguous-idx.c:24:3-24:29) [Info][Witness] witness generation summary: - total generation entries: 3 + total generation entries: 2 [Info][Race] Memory locations race summary: safe: 0 vulnerable: 0 @@ -18,20 +18,54 @@ total memory locations: 1 $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 68-ghost-ambiguous-idx.c - file_hash: $FILE_HASH - line: 20 - column: 3 - function: main - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 68-ghost-ambiguous-idx.c + file_hash: $FILE_HASH + line: 8 + column: 3 + function: t_fun + updates: [] + - location: + file_name: 68-ghost-ambiguous-idx.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + updates: [] + - location: + file_name: 68-ghost-ambiguous-idx.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 68-ghost-ambiguous-idx.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + updates: [] + - location: + file_name: 68-ghost-ambiguous-idx.c + file_hash: $FILE_HASH + line: 25 + column: 3 + function: main + updates: [] - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (0 <= data && data <= 1)' diff --git a/tests/regression/56-witness/69-ghost-ptr-protection.t b/tests/regression/56-witness/69-ghost-ptr-protection.t index 698f643385..0b28f22b0f 100644 --- a/tests/regression/56-witness/69-ghost-ptr-protection.t +++ b/tests/regression/56-witness/69-ghost-ptr-protection.t @@ -1,4 +1,4 @@ - $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_variable", "ghost_update"]' 69-ghost-ptr-protection.c + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 69-ghost-ptr-protection.c [Success][Assert] Assertion "*p != 0" will succeed (69-ghost-ptr-protection.c:26:3-26:27) [Info][Deadcode] Logical lines of code (LLoC) summary: live: 15 @@ -9,7 +9,7 @@ write with [lock:{m2}, thread:[main, t_fun@69-ghost-ptr-protection.c:22:3-22:40]] (conf. 110) (exp: & p) (69-ghost-ptr-protection.c:15:3-15:9) read with [mhp:{created={[main, t_fun@69-ghost-ptr-protection.c:22:3-22:40]}}, lock:{m1}, thread:[main]] (conf. 110) (exp: & p) (69-ghost-ptr-protection.c:26:3-26:27) [Info][Witness] witness generation summary: - total generation entries: 12 + total generation entries: 5 [Info][Race] Memory locations race summary: safe: 2 vulnerable: 0 @@ -19,66 +19,78 @@ Should not contain unsound flow-insensitive invariant m2_locked || (p == & g && *p == 0): $ yamlWitnessStrip < witness.yml - - entry_type: ghost_update - variable: multithreaded - expression: "1" - location: - file_name: 69-ghost-ptr-protection.c - file_hash: $FILE_HASH - line: 22 - column: 3 - function: main - - entry_type: ghost_update - variable: m2_locked - expression: "1" - location: - file_name: 69-ghost-ptr-protection.c - file_hash: $FILE_HASH - line: 13 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m2_locked - expression: "0" - location: - file_name: 69-ghost-ptr-protection.c - file_hash: $FILE_HASH - line: 16 - column: 3 - function: t_fun - - entry_type: ghost_update - variable: m1_locked - expression: "1" - location: - file_name: 69-ghost-ptr-protection.c - file_hash: $FILE_HASH - line: 23 - column: 3 - function: main - - entry_type: ghost_update - variable: m1_locked - expression: "0" - location: - file_name: 69-ghost-ptr-protection.c - file_hash: $FILE_HASH - line: 28 - column: 3 - function: main - - entry_type: ghost_variable - variable: multithreaded - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m2_locked - scope: global - type: int - initial: "0" - - entry_type: ghost_variable - variable: m1_locked - scope: global - type: int - initial: "0" + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m1_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: m2_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 16 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + updates: + - variable: m1_locked + value: "0" + format: c_expression - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (m2_locked || ((0 <= *p && *p <= 1) && p == & g))' From 554bd7f72e8cac3cc226976ad6f26bc434fd38d5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 17:01:40 +0200 Subject: [PATCH 526/566] Avoid empty ghost_instrumentation location updates --- src/analyses/mutexGhosts.ml | 7 ++- tests/regression/13-privatized/92-idx_priv.t | 28 ---------- .../56-witness/65-ghost-ambiguous-lock.t | 56 ------------------- .../56-witness/68-ghost-ambiguous-idx.t | 28 ---------- 4 files changed, 5 insertions(+), 114 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 6542ab3607..6ed0da4d4c 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -218,8 +218,11 @@ struct else (variables', updates) in - let location_update = WitnessGhost.location_update' ~node ~updates in - (variables', location_update :: location_updates) + match updates with + | [] -> (variables', location_updates) (* don't add location_update with no updates *) + | _ -> + let location_update = WitnessGhost.location_update' ~node ~updates in + (variables', location_update :: location_updates) ) nodes (VariableSet.empty, []) in let entry = WitnessGhost.instrumentation_entry ~task ~variables:(VariableSet.elements variables) ~location_updates in diff --git a/tests/regression/13-privatized/92-idx_priv.t b/tests/regression/13-privatized/92-idx_priv.t index 4783f65092..261108cf2f 100644 --- a/tests/regression/13-privatized/92-idx_priv.t +++ b/tests/regression/13-privatized/92-idx_priv.t @@ -23,20 +23,6 @@ value: "0" format: c_expression ghost_updates: - - location: - file_name: 92-idx_priv.c - file_hash: $FILE_HASH - line: 8 - column: 3 - function: t_fun - updates: [] - - location: - file_name: 92-idx_priv.c - file_hash: $FILE_HASH - line: 11 - column: 3 - function: t_fun - updates: [] - location: file_name: 92-idx_priv.c file_hash: $FILE_HASH @@ -47,20 +33,6 @@ - variable: multithreaded value: "1" format: c_expression - - location: - file_name: 92-idx_priv.c - file_hash: $FILE_HASH - line: 21 - column: 3 - function: main - updates: [] - - location: - file_name: 92-idx_priv.c - file_hash: $FILE_HASH - line: 23 - column: 3 - function: main - updates: [] - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (0 <= data && data <= 1)' diff --git a/tests/regression/56-witness/65-ghost-ambiguous-lock.t b/tests/regression/56-witness/65-ghost-ambiguous-lock.t index 8115bb2921..2771ec5c50 100644 --- a/tests/regression/56-witness/65-ghost-ambiguous-lock.t +++ b/tests/regression/56-witness/65-ghost-ambiguous-lock.t @@ -22,48 +22,6 @@ value: "0" format: c_expression ghost_updates: - - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 10 - column: 3 - function: t_fun - updates: [] - - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 13 - column: 3 - function: t_fun - updates: [] - - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 14 - column: 3 - function: t_fun - updates: [] - - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 17 - column: 3 - function: t_fun - updates: [] - - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 22 - column: 3 - function: fun - updates: [] - - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 24 - column: 3 - function: fun - updates: [] - location: file_name: 65-ghost-ambiguous-lock.c file_hash: $FILE_HASH @@ -74,20 +32,6 @@ - variable: multithreaded value: "1" format: c_expression - - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 35 - column: 3 - function: main - updates: [] - - location: - file_name: 65-ghost-ambiguous-lock.c - file_hash: $FILE_HASH - line: 37 - column: 3 - function: main - updates: [] - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (0 <= g2 && g2 <= 1)' diff --git a/tests/regression/56-witness/68-ghost-ambiguous-idx.t b/tests/regression/56-witness/68-ghost-ambiguous-idx.t index a8f2a0226a..e45399e005 100644 --- a/tests/regression/56-witness/68-ghost-ambiguous-idx.t +++ b/tests/regression/56-witness/68-ghost-ambiguous-idx.t @@ -28,20 +28,6 @@ value: "0" format: c_expression ghost_updates: - - location: - file_name: 68-ghost-ambiguous-idx.c - file_hash: $FILE_HASH - line: 8 - column: 3 - function: t_fun - updates: [] - - location: - file_name: 68-ghost-ambiguous-idx.c - file_hash: $FILE_HASH - line: 11 - column: 3 - function: t_fun - updates: [] - location: file_name: 68-ghost-ambiguous-idx.c file_hash: $FILE_HASH @@ -52,20 +38,6 @@ - variable: multithreaded value: "1" format: c_expression - - location: - file_name: 68-ghost-ambiguous-idx.c - file_hash: $FILE_HASH - line: 23 - column: 3 - function: main - updates: [] - - location: - file_name: 68-ghost-ambiguous-idx.c - file_hash: $FILE_HASH - line: 25 - column: 3 - function: main - updates: [] - entry_type: flow_insensitive_invariant flow_insensitive_invariant: string: '! multithreaded || (0 <= data && data <= 1)' From 2c25848a23e3b74859cd4b8a7be549d572a748bc Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 17:04:25 +0200 Subject: [PATCH 527/566] Remove support for old ghost_variable and ghost_update entry types --- src/analyses/mutexGhosts.ml | 48 -------------------- src/config/options.schema.json | 2 - src/witness/witnessGhost.ml | 16 +------ src/witness/yamlWitness.ml | 24 ++-------- src/witness/yamlWitnessType.ml | 67 ---------------------------- tests/util/yamlWitnessStripCommon.ml | 4 -- 6 files changed, 4 insertions(+), 157 deletions(-) diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 6ed0da4d4c..3deec3ef59 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -122,54 +122,6 @@ struct | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in begin match g with - | `Left g' when YamlWitness.entry_type_enabled YamlWitnessType.GhostVariable.entry_type && YamlWitness.entry_type_enabled YamlWitnessType.GhostUpdate.entry_type -> - let (locked, unlocked, multithread) = G.node (ctx.global g) in - let g = g' in - let entries = - (* TODO: do variable_entry-s only once *) - Locked.fold (fun l acc -> - match mustlock_of_addr l with - | Some l when ghost_var_available ctx (Locked l) -> - let entry = WitnessGhost.variable_entry ~task (Locked l) in - Queries.YS.add entry acc - | _ -> - acc - ) (Locked.union locked unlocked) (Queries.YS.empty ()) - in - let entries = - Locked.fold (fun l acc -> - match mustlock_of_addr l with - | Some l when ghost_var_available ctx (Locked l) -> - let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.one in - Queries.YS.add entry acc - | _ -> - acc - ) locked entries - in - let entries = - Unlocked.fold (fun l acc -> - match mustlock_of_addr l with - | Some l when ghost_var_available ctx (Locked l) -> - let entry = WitnessGhost.update_entry ~task ~node:g (Locked l) GoblintCil.zero in - Queries.YS.add entry acc - | _ -> - acc - ) unlocked entries - in - let entries = - if not (GobConfig.get_bool "exp.earlyglobs") && multithread then ( - if ghost_var_available ctx Multithreaded then ( - let entry = WitnessGhost.variable_entry ~task Multithreaded in - let entry' = WitnessGhost.update_entry ~task ~node:g Multithreaded GoblintCil.one in - Queries.YS.add entry (Queries.YS.add entry' entries) - ) - else - entries - ) - else - entries - in - entries | `Right true when YamlWitness.entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type -> let nodes = G.update (ctx.global g) in let (variables, location_updates) = NodeSet.fold (fun node (variables, location_updates) -> diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 8534620d02..362e028ee3 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -2673,8 +2673,6 @@ "precondition_loop_invariant_certificate", "invariant_set", "violation_sequence", - "ghost_variable", - "ghost_update", "ghost_instrumentation" ] }, diff --git a/src/witness/witnessGhost.ml b/src/witness/witnessGhost.ml index 3535e8a347..3eaa8ef69b 100644 --- a/src/witness/witnessGhost.ml +++ b/src/witness/witnessGhost.ml @@ -1,7 +1,7 @@ (** Ghost variables for YAML witnesses. *) let enabled () = - (YamlWitness.entry_type_enabled YamlWitnessType.GhostVariable.entry_type && YamlWitness.entry_type_enabled YamlWitnessType.GhostUpdate.entry_type) || YamlWitness.entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type + YamlWitness.entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type module Var = WitnessGhostVar @@ -11,20 +11,6 @@ module Map = RichVarinfo.BiVarinfoMap.Make (Var) include Map -let variable_entry ~task x = - let variable = name_varinfo x in - let type_ = String.trim (CilType.Typ.show (typ x)) in (* CIL printer puts space at the end of some types *) - let initial = CilType.Exp.show (initial x) in - YamlWitness.Entry.ghost_variable ~task ~variable ~type_ ~initial - -let update_entry ~task ~node x e = - let loc = Node.location node in - let location_function = (Node.find_fundec node).svar.vname in - let location = YamlWitness.Entry.location ~location:loc ~location_function in - let variable = name_varinfo x in - let expression = CilType.Exp.show e in - YamlWitness.Entry.ghost_update ~task ~location ~variable ~expression - let variable' x = let variable = name_varinfo x in let type_ = String.trim (CilType.Typ.show (typ x)) in (* CIL printer puts space at the end of some types *) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 81072ff82d..ec9a542919 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -149,25 +149,6 @@ struct metadata = metadata (); } - let ghost_variable ~task ~variable ~type_ ~(initial): Entry.t = { - entry_type = GhostVariable { - variable; - scope = "global"; - type_; - initial; - }; - metadata = metadata ~task (); - } - - let ghost_update ~task ~location ~variable ~(expression): Entry.t = { - entry_type = GhostUpdate { - variable; - expression; - location; - }; - metadata = metadata ~task (); - } - let ghost_variable' ~variable ~type_ ~(initial): GhostInstrumentation.Variable.t = { name = variable; scope = "global"; @@ -410,9 +391,10 @@ struct entries in - (* Generate flow-insensitive entries (ghost variables and ghost updates) *) + (* Generate flow-insensitive entries (ghost instrumentation) *) let entries = - if (entry_type_enabled YamlWitnessType.GhostVariable.entry_type && entry_type_enabled YamlWitnessType.GhostUpdate.entry_type) || entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type then ( + if entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type then ( + (* TODO: only at most one ghost_instrumentation entry can ever be produced, so this fold and deduplication is overkill *) let module EntrySet = Queries.YS in fst @@ GHT.fold (fun g v accs -> match g with diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 9b5d580849..bcd8e9435f 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -644,61 +644,6 @@ struct {content} end -module GhostVariable = -struct - type t = { - variable: string; - scope: string; - type_: string; - initial: string; - } - [@@deriving eq, ord, hash] - - let entry_type = "ghost_variable" - - let to_yaml' {variable; scope; type_; initial} = - [ - ("variable", `String variable); - ("scope", `String scope); - ("type", `String type_); - ("initial", `String initial); - ] - - let of_yaml y = - let open GobYaml in - let+ variable = y |> find "variable" >>= to_string - and+ scope = y |> find "scope" >>= to_string - and+ type_ = y |> find "type" >>= to_string - and+ initial = y |> find "initial" >>= to_string in - {variable; scope; type_; initial} -end - -module GhostUpdate = -struct - type t = { - variable: string; - expression: string; - location: Location.t; - } - [@@deriving eq, ord, hash] - - let entry_type = "ghost_update" - - let to_yaml' {variable; expression; location} = - [ - ("variable", `String variable); - ("expression", `String expression); - ("location", Location.to_yaml location); - ] - - let of_yaml y = - let open GobYaml in - let+ variable = y |> find "variable" >>= to_string - and+ expression = y |> find "expression" >>= to_string - and+ location = y |> find "location" >>= Location.of_yaml in - {variable; expression; location} -end - module GhostInstrumentation = struct @@ -831,8 +776,6 @@ struct | PreconditionLoopInvariantCertificate of PreconditionLoopInvariantCertificate.t | InvariantSet of InvariantSet.t | ViolationSequence of ViolationSequence.t - | GhostVariable of GhostVariable.t - | GhostUpdate of GhostUpdate.t | GhostInstrumentation of GhostInstrumentation.t [@@deriving eq, ord, hash] @@ -845,8 +788,6 @@ struct | PreconditionLoopInvariantCertificate _ -> PreconditionLoopInvariantCertificate.entry_type | InvariantSet _ -> InvariantSet.entry_type | ViolationSequence _ -> ViolationSequence.entry_type - | GhostVariable _ -> GhostVariable.entry_type - | GhostUpdate _ -> GhostUpdate.entry_type | GhostInstrumentation _ -> GhostInstrumentation.entry_type let to_yaml' = function @@ -858,8 +799,6 @@ struct | PreconditionLoopInvariantCertificate x -> PreconditionLoopInvariantCertificate.to_yaml' x | InvariantSet x -> InvariantSet.to_yaml' x | ViolationSequence x -> ViolationSequence.to_yaml' x - | GhostVariable x -> GhostVariable.to_yaml' x - | GhostUpdate x -> GhostUpdate.to_yaml' x | GhostInstrumentation x -> GhostInstrumentation.to_yaml' x let of_yaml y = @@ -889,12 +828,6 @@ struct else if entry_type = ViolationSequence.entry_type then let+ x = y |> ViolationSequence.of_yaml in ViolationSequence x - else if entry_type = GhostVariable.entry_type then - let+ x = y |> GhostVariable.of_yaml in - GhostVariable x - else if entry_type = GhostUpdate.entry_type then - let+ x = y |> GhostUpdate.of_yaml in - GhostUpdate x else if entry_type = GhostInstrumentation.entry_type then let+ x = y |> GhostInstrumentation.of_yaml in GhostInstrumentation x diff --git a/tests/util/yamlWitnessStripCommon.ml b/tests/util/yamlWitnessStripCommon.ml index d54dd446bf..39bc231d72 100644 --- a/tests/util/yamlWitnessStripCommon.ml +++ b/tests/util/yamlWitnessStripCommon.ml @@ -74,10 +74,6 @@ struct InvariantSet {content = List.sort InvariantSet.Invariant.compare (List.map invariant_strip_file_hash x.content)} (* Sort, so order is deterministic regardless of Goblint. *) | ViolationSequence x -> ViolationSequence {content = List.map segment_strip_file_hash x.content} - | GhostVariable x -> - GhostVariable x (* no location to strip *) - | GhostUpdate x -> - GhostUpdate {x with location = location_strip_file_hash x.location} | GhostInstrumentation x -> GhostInstrumentation { (* Sort, so order is deterministic regardless of Goblint. *) ghost_variables = List.sort GhostInstrumentation.Variable.compare x.ghost_variables; From 0ca1bb30f50d12bec84198ae404994c510e37431 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 22 Nov 2024 11:11:34 +0200 Subject: [PATCH 528/566] Add parsing of integer constraints in YAML violation_sequence-s --- src/util/std/gobYaml.ml | 2 ++ src/witness/yamlWitnessType.ml | 26 +++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/util/std/gobYaml.ml b/src/util/std/gobYaml.ml index 624cdbf1fa..4c8576ade2 100644 --- a/src/util/std/gobYaml.ml +++ b/src/util/std/gobYaml.ml @@ -44,3 +44,5 @@ let list = function let entries = function | `O assoc -> Ok assoc | _ -> Error (`Msg "Failed to get entries from non-object value") + +let int i = float (float_of_int i) diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 4fc2029801..c77fadad4c 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -447,15 +447,35 @@ struct module Constraint = struct + + module Value = + struct + type t = + | String of string + | Int of int (* Why doesn't format consider ints (for switch branches) as strings here, like everywhere else? *) + [@@deriving ord] + + let to_yaml = function + | String s -> GobYaml.string s + | Int i -> GobYaml.int i + + let of_yaml y = + let open GobYaml in + match y with + | `String s -> Ok (String s) + | `Float f -> Ok (Int (int_of_float f)) + | _ -> Error (`Msg "Expected a string or integer value") + end + type t = { - value: string; + value: Value.t; format: string option; } [@@deriving ord] let to_yaml {value; format} = `O ([ - ("value", `String value); + ("value", Value.to_yaml value); ] @ (match format with | Some format -> [ ("format", `String format); @@ -466,7 +486,7 @@ struct let of_yaml y = let open GobYaml in - let+ value = y |> find "value" >>= to_string + let+ value = y |> find "value" >>= Value.of_yaml and+ format = y |> Yaml.Util.find "format" >>= option_map to_string in {value; format} end From d7074f1e9526c0df5f146d267c35038b2bb65770 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 27 Nov 2024 13:44:50 +0200 Subject: [PATCH 529/566] Add Karoliine's email to opam maintainer field opam-repository CI now demands this. Co-authored-by: Karoliine Holter --- dune-project | 2 +- goblint.opam | 2 +- goblint.opam.locked | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dune-project b/dune-project index 54915cf964..f2f87b3c58 100644 --- a/dune-project +++ b/dune-project @@ -16,7 +16,7 @@ (homepage "https://goblint.in.tum.de") (documentation "https://goblint.readthedocs.io/en/latest/") (authors "Simmo Saan" "Michael Schwarz" "Julian Erhard" "Sarah Tilscher" "Karoliine Holter" "Ralf Vogler" "Kalmer Apinis" "Vesal Vojdani" ) ; same authors as in .zenodo.json and CITATION.cff -(maintainers "Simmo Saan " "Michael Schwarz " "Karoliine Holter") +(maintainers "Simmo Saan " "Michael Schwarz " "Karoliine Holter ") (license MIT) (package diff --git a/goblint.opam b/goblint.opam index 44e5ccd2c2..f74ffab8c4 100644 --- a/goblint.opam +++ b/goblint.opam @@ -9,7 +9,7 @@ Goblint includes analyses for assertions, overflows, deadlocks, etc and can be e maintainer: [ "Simmo Saan " "Michael Schwarz " - "Karoliine Holter" + "Karoliine Holter " ] authors: [ "Simmo Saan" diff --git a/goblint.opam.locked b/goblint.opam.locked index 9fbee1e02b..cedb4088b8 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -5,7 +5,7 @@ synopsis: "Static analysis framework for C" maintainer: [ "Simmo Saan " "Michael Schwarz " - "Karoliine Holter" + "Karoliine Holter " ] authors: [ "Simmo Saan" From 65ddbbb7d06236e2a266ef2967f58c59528a48c6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Nov 2024 10:27:35 +0200 Subject: [PATCH 530/566] Finalize CHANGELOG for v2.5.0 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf6a8aa781..1fb07a7dc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## v2.5.0 (unreleased) +## v2.5.0 Functionally equivalent to Goblint in SV-COMP 2025. * Add 32bit vs 64bit architecture support (#54, #1574). From 629cd493201c133e284f45437816ab82fe305742 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Nov 2024 10:34:13 +0200 Subject: [PATCH 531/566] Replace goblint-cil pin with published 2.0.5 --- dune-project | 2 +- goblint.opam | 5 +++-- goblint.opam.locked | 6 +----- goblint.opam.template | 3 ++- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/dune-project b/dune-project index f2f87b3c58..9a1d958484 100644 --- a/dune-project +++ b/dune-project @@ -37,7 +37,7 @@ Goblint includes analyses for assertions, overflows, deadlocks, etc and can be e "concurrency")) (depends (ocaml (>= 4.14)) - (goblint-cil (>= 2.0.4)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. + (goblint-cil (>= 2.0.5)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. (batteries (>= 3.5.1)) (zarith (>= 1.10)) (yojson (>= 2.0.0)) diff --git a/goblint.opam b/goblint.opam index f74ffab8c4..9fa877d54f 100644 --- a/goblint.opam +++ b/goblint.opam @@ -37,7 +37,7 @@ bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ "dune" {>= "3.7"} "ocaml" {>= "4.14"} - "goblint-cil" {>= "2.0.4"} + "goblint-cil" {>= "2.0.5"} "batteries" {>= "3.5.1"} "zarith" {>= "1.10"} "yojson" {>= "2.0.0"} @@ -97,7 +97,8 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" ] + # published goblint-cil 2.0.5 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release diff --git a/goblint.opam.locked b/goblint.opam.locked index cedb4088b8..081731a9a3 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -64,7 +64,7 @@ depends: [ "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} - "goblint-cil" {= "2.0.4"} + "goblint-cil" {= "2.0.5"} "hex" {= "1.5.0"} "integers" {= "0.7.0"} "json-data-encoding" {= "1.0.1"} @@ -138,10 +138,6 @@ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] pin-depends: [ - [ - "goblint-cil.2.0.4" - "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" - ] [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" diff --git a/goblint.opam.template b/goblint.opam.template index 0a517fbfa0..d05a0af61d 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -2,7 +2,8 @@ # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" ] + # published goblint-cil 2.0.5 is currently up-to-date, so no pin needed + # [ "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release From d066c8dd711317ae969639d45285aa5664767daa Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Nov 2024 10:35:32 +0200 Subject: [PATCH 532/566] Disable pins for v2.5.0 release --- goblint.opam | 8 ++++---- goblint.opam.locked | 10 ---------- goblint.opam.template | 8 ++++---- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/goblint.opam b/goblint.opam index 9fa877d54f..9f2b874ff6 100644 --- a/goblint.opam +++ b/goblint.opam @@ -96,14 +96,14 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") -pin-depends: [ +# pin-depends: [ # published goblint-cil 2.0.5 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release - [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] + # [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release - [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] -] + # [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] +# ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} ] diff --git a/goblint.opam.locked b/goblint.opam.locked index 081731a9a3..3a7bb1bfa5 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -137,16 +137,6 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] -pin-depends: [ - [ - "camlidl.1.12" - "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" - ] - [ - "apron.v0.9.15" - "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" - ] -] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} description: """\ Goblint is a sound static analysis framework for C programs using abstract interpretation. diff --git a/goblint.opam.template b/goblint.opam.template index d05a0af61d..8766a89df2 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,14 +1,14 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") -pin-depends: [ +# pin-depends: [ # published goblint-cil 2.0.5 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release - [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] + # [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release - [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] -] + # [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] +# ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} ] From 0df4d8647afbfd2d65043c13f89047ecc3a2219b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Nov 2024 13:36:28 +0200 Subject: [PATCH 533/566] Update goblint-cil to 2.0.5 in Gobview lock file --- gobview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gobview b/gobview index 76e42c34d3..8e1b755ebc 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 76e42c34d36bd2ab6900efd661a972ba4824f065 +Subproject commit 8e1b755ebc5fb479095fb4dcc30305fe02501e47 From eb9ee513ba2cb1811750d58fd10370f31c21dda1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Nov 2024 14:52:54 +0200 Subject: [PATCH 534/566] Make 29-svcomp/36-svcomp-arch multilib detection more precise Also handles missing gcc-multilib on Linux, e.g. in opam docker. There's no conf-* package for gcc-multilib. --- tests/regression/29-svcomp/dune | 2 +- tests/util/dune | 7 ++++++- tests/util/multilibConfigure.ml | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 tests/util/multilibConfigure.ml diff --git a/tests/regression/29-svcomp/dune b/tests/regression/29-svcomp/dune index 95ac66a5ec..9b2396b313 100644 --- a/tests/regression/29-svcomp/dune +++ b/tests/regression/29-svcomp/dune @@ -17,4 +17,4 @@ (cram (applies_to 36-svcomp-arch) - (enabled_if (<> %{system} macosx))) ; https://dune.readthedocs.io/en/stable/reference/boolean-language.html + (enabled_if %{read:../../util/multilibAvailable})) ; https://dune.readthedocs.io/en/stable/reference/boolean-language.html diff --git a/tests/util/dune b/tests/util/dune index 0e32304d4f..e43d21c25d 100644 --- a/tests/util/dune +++ b/tests/util/dune @@ -1,7 +1,8 @@ (executables - (names yamlWitnessStrip yamlWitnessStripDiff) + (names yamlWitnessStrip yamlWitnessStripDiff multilibConfigure) (libraries batteries.unthreaded + goblint-cil goblint_std goblint_lib yaml @@ -9,3 +10,7 @@ goblint.build-info.dune) (flags :standard -open Goblint_std) (preprocess (pps ppx_deriving.std))) + +(rule + (target multilibAvailable) + (action (with-stdout-to %{target} (run ./multilibConfigure.exe)))) diff --git a/tests/util/multilibConfigure.ml b/tests/util/multilibConfigure.ml new file mode 100644 index 0000000000..cf59e04416 --- /dev/null +++ b/tests/util/multilibConfigure.ml @@ -0,0 +1,4 @@ +open GoblintCil + +let () = + Printf.printf "%B" (Option.is_some GoblintCil.Machdep.gcc32 && Option.is_some GoblintCil.Machdep.gcc64) From 7170d9a8944706a1adc0acaeb81a4fc6d914af7b Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 28 Nov 2024 15:00:16 +0200 Subject: [PATCH 535/566] Fix unused open in multilibConfigure --- tests/util/multilibConfigure.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/util/multilibConfigure.ml b/tests/util/multilibConfigure.ml index cf59e04416..96cf9a706a 100644 --- a/tests/util/multilibConfigure.ml +++ b/tests/util/multilibConfigure.ml @@ -1,4 +1,4 @@ open GoblintCil let () = - Printf.printf "%B" (Option.is_some GoblintCil.Machdep.gcc32 && Option.is_some GoblintCil.Machdep.gcc64) + Printf.printf "%B" (Option.is_some Machdep.gcc32 && Option.is_some Machdep.gcc64) From 4f83ce8369977071c5a749ad50cf5ebba7aa4f75 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Nov 2024 10:51:02 +0200 Subject: [PATCH 536/566] Revert "Disable pins for v2.5.0 release" This reverts commit d066c8dd711317ae969639d45285aa5664767daa. --- goblint.opam | 8 ++++---- goblint.opam.locked | 10 ++++++++++ goblint.opam.template | 8 ++++---- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/goblint.opam b/goblint.opam index 9f2b874ff6..9fa877d54f 100644 --- a/goblint.opam +++ b/goblint.opam @@ -96,14 +96,14 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") -# pin-depends: [ +pin-depends: [ # published goblint-cil 2.0.5 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release - # [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] + [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release - # [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] -# ] + [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] +] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} ] diff --git a/goblint.opam.locked b/goblint.opam.locked index 3a7bb1bfa5..081731a9a3 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -137,6 +137,16 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +pin-depends: [ + [ + "camlidl.1.12" + "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" + ] + [ + "apron.v0.9.15" + "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" + ] +] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} description: """\ Goblint is a sound static analysis framework for C programs using abstract interpretation. diff --git a/goblint.opam.template b/goblint.opam.template index 8766a89df2..d05a0af61d 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,14 +1,14 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") -# pin-depends: [ +pin-depends: [ # published goblint-cil 2.0.5 is currently up-to-date, so no pin needed # [ "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release - # [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] + [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release - # [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] -# ] + [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] +] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} ] From 77acd917865a4385c160155740d20615c0e87f2a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Nov 2024 10:58:11 +0200 Subject: [PATCH 537/566] Pin released goblint-cil.2.0.5 for reproducibility --- goblint.opam | 4 ++-- goblint.opam.locked | 4 ++++ goblint.opam.template | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/goblint.opam b/goblint.opam index 9fa877d54f..219c67d011 100644 --- a/goblint.opam +++ b/goblint.opam @@ -97,8 +97,8 @@ dev-repo: "git+https://github.com/goblint/analyzer.git" # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - # published goblint-cil 2.0.5 is currently up-to-date, so no pin needed - # [ "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] + # published goblint-cil 2.0.5 is currently up-to-date, but pinned for reproducibility + [ "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release diff --git a/goblint.opam.locked b/goblint.opam.locked index 081731a9a3..2594aea288 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -138,6 +138,10 @@ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] pin-depends: [ + [ + "oblint-cil.2.0.5" + "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" + ] [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" diff --git a/goblint.opam.template b/goblint.opam.template index d05a0af61d..84dcc24d8d 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -2,8 +2,8 @@ # also remember to generate/adjust goblint.opam.locked! available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - # published goblint-cil 2.0.5 is currently up-to-date, so no pin needed - # [ "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] + # published goblint-cil 2.0.5 is currently up-to-date, but pinned for reproducibility + [ "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release From dbdefab9825123e08c92b4c36618e7eacb7883d4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Nov 2024 11:21:22 +0200 Subject: [PATCH 538/566] Fix must-double-locking in regression tests --- tests/regression/04-mutex/32-allfuns.c | 4 ++-- tests/regression/09-regions/31-equ_rc.c | 2 +- tests/regression/09-regions/32-equ_nr.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/regression/04-mutex/32-allfuns.c b/tests/regression/04-mutex/32-allfuns.c index b59c487c53..e6b88e47ce 100644 --- a/tests/regression/04-mutex/32-allfuns.c +++ b/tests/regression/04-mutex/32-allfuns.c @@ -8,11 +8,11 @@ pthread_mutex_t B_mutex = PTHREAD_MUTEX_INITIALIZER; void t1() { pthread_mutex_lock(&A_mutex); myglobal++; //RACE! - pthread_mutex_lock(&A_mutex); + pthread_mutex_unlock(&A_mutex); } void t2() { pthread_mutex_lock(&B_mutex); myglobal++; //RACE! - pthread_mutex_lock(&B_mutex); + pthread_mutex_unlock(&B_mutex); } diff --git a/tests/regression/09-regions/31-equ_rc.c b/tests/regression/09-regions/31-equ_rc.c index 7cea370c58..2f3aff7f63 100644 --- a/tests/regression/09-regions/31-equ_rc.c +++ b/tests/regression/09-regions/31-equ_rc.c @@ -15,7 +15,7 @@ struct s { void *t_fun(void *arg) { pthread_mutex_lock(&A.mutex); B.datum = 5; // RACE! - pthread_mutex_lock(&A.mutex); + pthread_mutex_unlock(&A.mutex); return NULL; } diff --git a/tests/regression/09-regions/32-equ_nr.c b/tests/regression/09-regions/32-equ_nr.c index d9b909546e..a242448ba8 100644 --- a/tests/regression/09-regions/32-equ_nr.c +++ b/tests/regression/09-regions/32-equ_nr.c @@ -15,7 +15,7 @@ struct s { void *t_fun(void *arg) { pthread_mutex_lock(&A.mutex); A.datum = 5; // NORACE - pthread_mutex_lock(&A.mutex); + pthread_mutex_unlock(&A.mutex); return NULL; } From 218770b8f3f825338db1e10911668151d3317f14 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Nov 2024 11:21:55 +0200 Subject: [PATCH 539/566] Check must-double-locking in 03-practical/21-pfscan_combine_minimal --- tests/regression/03-practical/21-pfscan_combine_minimal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/03-practical/21-pfscan_combine_minimal.c b/tests/regression/03-practical/21-pfscan_combine_minimal.c index abdef0627b..86cfdabac1 100644 --- a/tests/regression/03-practical/21-pfscan_combine_minimal.c +++ b/tests/regression/03-practical/21-pfscan_combine_minimal.c @@ -18,7 +18,7 @@ int pqueue_init(PQUEUE *qp) void pqueue_close(PQUEUE *qp ) { - pthread_mutex_lock(& qp->mtx); + pthread_mutex_lock(& qp->mtx); // WARN (must double locking) qp->closed = 1; pthread_mutex_unlock(& qp->mtx); return; @@ -26,7 +26,7 @@ void pqueue_close(PQUEUE *qp ) int pqueue_put(PQUEUE *qp) { - pthread_mutex_lock(& qp->mtx); + pthread_mutex_lock(& qp->mtx); // WARN (must double locking) if (qp->closed) { // pfscan actually has a bug and is missing the following unlock at early return // pthread_mutex_unlock(& qp->mtx); From 68cd95237ebbb2023f6c9f7e59dc5f2d33d8ad45 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Nov 2024 11:30:41 +0200 Subject: [PATCH 540/566] Fix goblint-cil typo in opam lock file --- goblint.opam.locked | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goblint.opam.locked b/goblint.opam.locked index 2594aea288..e5176b9007 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -139,7 +139,7 @@ post-messages: [ ] pin-depends: [ [ - "oblint-cil.2.0.5" + "goblint-cil.2.0.5" "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] [ From ffe255b9a60a1c4919b565f6c9a0e07c47ac7218 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Nov 2024 14:11:46 +0200 Subject: [PATCH 541/566] Count witness.invariant.flow_insensitive-as location invariants in summary --- src/witness/yamlWitness.ml | 2 ++ tests/regression/13-privatized/04-priv_multi.t | 2 +- tests/regression/13-privatized/74-mutex.t | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index e3978f9929..9d04b597fa 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -385,6 +385,7 @@ struct fold_flow_insensitive_as_location ~inv (fun ~location ~inv acc -> let invariant = Entry.invariant (CilType.Exp.show inv) in let entry = Entry.location_invariant ~task ~location ~invariant in + incr cnt_location_invariant; entry :: acc ) acc | `Lifted _, _ @@ -605,6 +606,7 @@ struct fold_flow_insensitive_as_location ~inv (fun ~location ~inv acc -> let invariant = CilType.Exp.show inv in let invariant = Entry.location_invariant' ~location ~invariant in + incr cnt_location_invariant; invariant :: acc ) acc | `Bot | `Top -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) diff --git a/tests/regression/13-privatized/04-priv_multi.t b/tests/regression/13-privatized/04-priv_multi.t index af7c9b2098..1f6dff3fdc 100644 --- a/tests/regression/13-privatized/04-priv_multi.t +++ b/tests/regression/13-privatized/04-priv_multi.t @@ -215,7 +215,7 @@ Flow-insensitive invariants as location invariants. [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:45:10-45:11) [Warning][Deadcode][CWE-571] condition 'B > 0' is always true (04-priv_multi.c:47:9-47:14) [Info][Witness] witness generation summary: - location invariants: 0 + location invariants: 9 loop invariants: 0 flow-insensitive invariants: 0 total generation entries: 10 diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t index 1d750a211c..4b370db387 100644 --- a/tests/regression/13-privatized/74-mutex.t +++ b/tests/regression/13-privatized/74-mutex.t @@ -108,7 +108,7 @@ Flow-insensitive invariants as location invariants. total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: - location invariants: 0 + location invariants: 2 loop invariants: 0 flow-insensitive invariants: 0 total generation entries: 3 @@ -177,7 +177,7 @@ Same with ghost_instrumentation and invariant_set entries. total lines: 15 [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) [Info][Witness] witness generation summary: - location invariants: 0 + location invariants: 2 loop invariants: 0 flow-insensitive invariants: 0 total generation entries: 2 From f3dfca7a9ab28fe16ce77bfeac0d55f65058ff4c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 29 Nov 2024 17:25:15 +0200 Subject: [PATCH 542/566] Change must-double-locking in 03-practical/21-pfscan_combine_minimal to TODO On MacOS the mutex type is top because it's zero-initialized global. On MacOS zero-initialized mutex isn't the same as a default mutex (type 0). --- tests/regression/03-practical/21-pfscan_combine_minimal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regression/03-practical/21-pfscan_combine_minimal.c b/tests/regression/03-practical/21-pfscan_combine_minimal.c index 86cfdabac1..b8fc7948b2 100644 --- a/tests/regression/03-practical/21-pfscan_combine_minimal.c +++ b/tests/regression/03-practical/21-pfscan_combine_minimal.c @@ -18,7 +18,7 @@ int pqueue_init(PQUEUE *qp) void pqueue_close(PQUEUE *qp ) { - pthread_mutex_lock(& qp->mtx); // WARN (must double locking) + pthread_mutex_lock(& qp->mtx); // TODO (OSX): WARN (must double locking) qp->closed = 1; pthread_mutex_unlock(& qp->mtx); return; @@ -26,7 +26,7 @@ void pqueue_close(PQUEUE *qp ) int pqueue_put(PQUEUE *qp) { - pthread_mutex_lock(& qp->mtx); // WARN (must double locking) + pthread_mutex_lock(& qp->mtx); // TODO (OSX): WARN (must double locking) if (qp->closed) { // pfscan actually has a bug and is missing the following unlock at early return // pthread_mutex_unlock(& qp->mtx); From 0706c136b7897191ea1241d76f7c3dc716d939a1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 12:14:35 +0200 Subject: [PATCH 543/566] Copy VMCAI 2025 artifact description from concurrency-witnesses repo --- docs/artifact-descriptions/vmcai25.md | 288 ++++++++++++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 289 insertions(+) create mode 100644 docs/artifact-descriptions/vmcai25.md diff --git a/docs/artifact-descriptions/vmcai25.md b/docs/artifact-descriptions/vmcai25.md new file mode 100644 index 0000000000..08b2c468b8 --- /dev/null +++ b/docs/artifact-descriptions/vmcai25.md @@ -0,0 +1,288 @@ +# Artifact for VMCAI'2025 Paper "Correctness Witnesses for Concurrent Programs: Bridging the Semantic Divide with Ghosts" + +------------------------------------------------------------------------------- + +## OVERVIEW + +This artifact contains the following components: + + EVALUATION RESULTS :: The evaluation results, and overview tables (HTML) + generated from the raw data. + SOURCE CODE :: Source code for Goblint and Ultimate GemCutter, + the verification tools used in the experiments + for the paper. + VERIFIER BINARIES :: Binaries for Goblint and Ultimate GemCutter. + BENCHMARK PROGRAMS :: Benchmarks used for evaluation of the verifiers. + BENCHMARK WITNESSES :: The witnesses generated by Goblint, as used in the + experiments. + BENCHEXEC TOOL :: The BenchExec benchmarking tool can be used + to replicate our results on these benchmarks. + +The next section gives instructions on how to setup and quickly get an overview of the artifact. +The subsequent sections then describe each of these components in detail. +The final section gives information on how to reuse this artifact. + +------------------------------------------------------------------------------- + +## GETTING STARTED + +### 1. Setup + +The artifact is a virtual machine (VM). Follow these steps to set it up: + +* If you have not done so already, install VirtualBox. + (https://www.virtualbox.org/wiki/Downloads) +* Download the artifact. +* Import the artifact via the VirtualBox UI (`File > Import Appliance`) + or by running `VBoxManage import ghost-witnesses.ova`. + +You can then start the VM from the VirtualBox UI. +Login with user `vagrant` and password `vagrant`. +(Note: By default, the user `Ubuntu` may be selected on the login screen. Click on the name and switch to user `vagrant`.) + +You may want to install VirtualBox guest additions (). +If the usual installation does not work, try the following steps: + +* Add a disk drive to the VM in its settings (the VM must be off for this). +* After starting the VM and logging in, select `Devices > Insert Guest Additions CD image` from the VirtualBox menu. +* Run the following in a terminal: + `sudo mount /dev/cdrom /mnt && cd /mnt && sudo ./VBoxLinuxAdditions.run` + +### 2. Inspect the evaluation results + +To extract the table data used in the paper from our raw evaluation results, open a terminal in the VM (`Ctrl+Alt+T`) and run the following commands: + + ~/scripts/analyse-witnesses.py "$HOME/witness-generation/paper-evaluation/goblint.2024-09-02_08-21-23.files" + cd ~/witness-validation/paper-evaluation/ + WITNESS_DIR="$HOME/witness-generation/paper-evaluation/goblint.2024-09-02_08-21-23.files" ~/scripts/analyse-validation.py + +The times for the Table 2 are given first in seconds and then pretty-printed as in the paper. + +For a more detailed inspection and visualization of the data, take a look at the generated HTML report. +Just open the file `~/witness-validation/paper-evaluation/results.2024-09-24_22-00-48.table.html` in firefox. +For detailed information, see the "EVALUATION RESULTS" section below. + +### 3. Quick Test of the Benchmark Setup + +To analyse some programs with Goblint and analyse the generated witnesses with GemCutter, run + + ~/scripts/quick-run.sh + +on a console in the VM. +This will analyse a single program with Goblint and generate witnesses using the four analyses described in the paper. +Subsequently, the script will run GemCutter's verification, witness confirmation and witness validation analyses on the example. + +The whole run should should conclude successfully for all configurations in about 2min. +This is indicated by the benchmark results `true` printed on the console, and the fact that the final output looks roughly like this: + + Table 1: Witness Confirmation + ============================= + correct programs + ----------------------------- + protection-ghost : total= 1 , confirmed= 1 , rejected= 0 , confirmation rate= 100.0 % , resource out= 0 + mutex-meet-ghost : total= 1 , confirmed= 1 , rejected= 0 , confirmation rate= 100.0 % , resource out= 0 + protection-local : total= 1 , confirmed= 1 , rejected= 0 , confirmation rate= 100.0 % , resource out= 0 + mutex-meet-local : total= 1 , confirmed= 1 , rejected= 0 , confirmation rate= 100.0 % , resource out= 0 + + confirmation range: 100.0 % - 100.0 % + + incorrect programs + ----------------------------- + No programs with witnesses found for protection-ghost. Skipping benchmark set... + No programs with witnesses found for mutex-meet-ghost. Skipping benchmark set... + No programs with witnesses found for protection-local. Skipping benchmark set... + No programs with witnesses found for mutex-meet-local. Skipping benchmark set... + + confirmation range: 100.0 % - 0.0 % + + + Table 2: Witness Validation + =========================== + protection-ghost + ---------------- + validation : {'number': 1, 'time': 14, 'time_pretty': 0:00:14} + verification : {'number': 1, 'time': 12, 'time_pretty': 0:00:12} + 0 tasks could be validated but not verified + + mutex-meet-ghost + ---------------- + validation : {'number': 1, 'time': 13, 'time_pretty': 0:00:13} + verification : {'number': 1, 'time': 12, 'time_pretty': 0:00:12} + 0 tasks could be validated but not verified + + protection-local + ---------------- + validation : {'number': 1, 'time': 12, 'time_pretty': 0:00:12} + verification : {'number': 1, 'time': 12, 'time_pretty': 0:00:12} + 0 tasks could be validated but not verified + + mutex-meet-local + ---------------- + validation : {'number': 1, 'time': 13, 'time_pretty': 0:00:13} + verification : {'number': 1, 'time': 12, 'time_pretty': 0:00:12} + 0 tasks could be validated but not verified + + ~ + + =============================================================================== + Completed quick benchmark test run. + + Results of witness generation can be found in: /home/vagrant/witness-generation/2024-10-01_08-57-09 + Generated witnesses are located in: /home/vagrant/witness-generation/2024-10-01_08-57-09/goblint.2024-10-01_08-57-09.files + Results of witness validation can be found in: /home/vagrant/witness-validation/2024-10-01_08-57-09 + =============================================================================== + +For a slightly larger set of experiments, run + + ~/scripts/medium-run.sh + +This will run an entire folder of SV-COMP benchmarks through Goblint and subsequently analyse the generated witness with GemCutter. +The whole run should complete in 3-4h. +(Note: This run uses a smaller timeout and memory limit than the full evaluation used in the paper, so the results for individual benchmarks may differ.) + +#### Troubleshooting +On certain old host machines, GemCutter fails with `ERROR(7)`, and the log files (`/home/vagrant/witness-validation/YYYY-MM-DD_hh-mm-ss/concurrency-witness-validation-gemcutter.YYYY-MM-DD_hh-mm-ss.logfiles/*.log`) contain a message as follows: + + [2024-10-01 22:04:14,025 INFO L327 MonitoredProcess]: [MP /home/vagrant/ultimate/releaseScripts/default/UGemCutter-linux/z3 SMTLIB2_COMPLIANT=true -memory:2024 -smt2 -in -t:2000 (1)] Waiting until timeout for monitored process + [2024-10-01 22:04:14,058 FATAL L? ?]: An unrecoverable error occured during an interaction with an SMT solver: + de.uni_freiburg.informatik.ultimate.logic.SMTLIBException: External (MP /home/vagrant/ultimate/releaseScripts/default/UGemCutter-linux/z3 SMTLIB2_COMPLIANT=true -memory:2024 -smt2 -in -t:2000 (1) with exit command (exit)) Received EOF on stdin. No stderr output. + +This is because the version of Z3 shipped with GemCutter uses certain processor instructions that the host does not support or VirtualBox emulates incorrectly. +In this case, download an official build of Z3 () and replace the file `~/ultimate/releaseScripts/default/UGemCutter-linux/z3` with the corresponding binary from the official ZIP: + + wget https://github.com/Z3Prover/z3/releases/download/z3-4.12.5/z3-4.12.5-x64-glibc-2.31.zip + unzip z3-4.12.5-x64-glibc-2.31.zip + cp z3-4.12.5-x64-glibc-2.31/bin/z3 ~/ultimate/releaseScripts/default/UGemCutter-linux/z3 + +### 4. Running the Full Experiments + +To re-run the full experiments, execute + + ~/scripts/full-run.sh + +This script behaves similarly to the smaller variants in the previous sections. +Note however: + +- By default the experiments require 16 GB of memory per benchmark (this configuration was used for the experiments in the paper). + To reproduce this, you will have to modify the VM's settings in VirtualBox and increase the available memory (shutdown the machine while doing so). + + Alternatively, you can run the experiments with a reduced memory limit. + To do so, modify the environment variable `BENCHMARK_PARAMS`. + For instance, the following allows only 4GB of memory per benchmark: + + BENCHMARK_PARAMS="-M 4GB" ~/scripts/full-run.sh + +- The full evaluation for the paper required around 3 days. + In this evaluation, we used the benchexec tool to run 14 validation tasks in parallel (occupying up to 28 cores at a time). + + By default, the provided script only runs one benchmark at a time. + If you have sufficient cores and memory available (adjust the VM settings accordingly), you can run multiple benchmarks in parallel by setting the environment variable `BENCHEXEC_THREADS`. You may also execute the experiments with a reduced timeout. + For instance, the following command runs 4 benchmarks in parallel at a time (occupying up to 8 cores), and gives each benchmark a 300s timeout and 4GB memory limit: + + BENCHEXEC_THREADS=4 BENCHMARK_PARAMS="-T 300s -M 4GB" ~/scripts/full-run.sh + +Naturally, changes to the timeout or memory are expected to affect the evaluation numbers. + +---------------------------------------------------------------------------------- + +## EVALUATION RESULTS + +The evaluation results that form the basis for the experimental data in the paper can be found in the directory `~/witness-validation/paper-evaluation/`. +The witnesses generated by Goblint that formed the basis for this evaluation can be found in `~/witness-generation/paper-evaluation/`. +See below for detailed info to reproduce the tables and figures of the paper. + +The file `~/witness-validation/paper-evaluation/~/witness-validation/paper-evaluation/` contains an HTML overview page generated by the BenchExec benchmarking tool, which displays individual results, quantile and scatter plots. Through the filtering sidebar (top right corner), detailed analyses can be made. + +The table contains the following configurations : + +* `verify` -- GemCutter verification, without any witness +* `verify+validate-goblint-witnesses-{mutex-meet,protection}-{ghost,local}` -- GemCutter witness validation, applied to the 4 different witness sets generated by Goblint. + In this mode, GemCutter checks if the given witness is valid and the corresponding program is correct. +* `validate-goblint-witnesses-{mutex-meet,protection}-{ghost,local}` -- GemCutter witness _confirmation_, applied to the different witness sets. + In this mode, described in the paper, GemCutter only checks if the given witness' invariants are correct, but does not prove the corresponding program correct. + +The summary table shows how many benchmarks were analysed and the results. +- The row `correct true` indicates tasks that were successfully verified (for `verify`), or where the witness was confirmed resp. validated (for the other configurations). +- The row `correct false` indicates that a bug was found (for `verify`), resp. that witness validation failed and a witness was rejected. + The latter only happens for programs that are incorrect, hence there can be no valid correctness witness and rejection is expected. + As witness validation is not possible for incorrect programs, this data is not discussed in the paper and only appears here due to the benchmark setup. +- The row `incorrect false` would indicate that GemCutter finds a supposed bug in a correct program (for `verify`). + For witness confirmation configurations, it indicates that GemCutter confirmed the correctness witness given by Goblint, for an incorrect program. + As witness confirmation ignores the program's correctness, these results are expected and do not indicate a problem in one of the tools. + +The *Table* tab gives access to detailed evaluation results for each file. +Clicking on a status shows the complete GemCutter log for the benchmark run. + +> **Note:** If you are trying to view logs for individual runs through the HTML table (by clicking on the evaluation result `true` or `false`), you may encounter a warning because browsers block access to local files. Follow the instructions in the message to enable log viewing. + +As described above (in section _2. Inspect the evaluation results_), the artifact provides python scripts to directly extract the data shown in the paper from the benchmark results. + + +---------------------------------------------------------------------------------- + +## SOURCE CODE + +### GemCutter + +Ultimate GemCutter is developed as part of the Ultimate program analysis framework () and is implemented in Java. The source code for Ultimate at the time of evaluation can be found in this artifact in the `~/ultimate` directory. + +The directory `trunk/source/CACSL2BoogieTranslator/src/de/uni_freiburg/informatik/ultimate/plugins/generator/cacsl2boogietranslator/witness` is of particular interest for the present paper. +The code in this directory handles instrumentation of the program with various witness entries, as part of Ultimate's translation of the original C code to the intermediate verification language Boogie. + +More recent versions of Ultimate can be found at . + +### Goblint + +The Goblint analyzer () is developed by TU Munich and University of Tartu. The source code for Goblint at the time of evaluation can be found in this artifact in the `~/goblint` directory. + +More recent versions of Goblint can be found at . + +---------------------------------------------------------------------------------- + +## VERIFIER BINARIES + +A pre-built binary of GemCutter can be found in `~/ultimate/releaseScripts/default/UGemCutter-linux/`. +For information on how to execute GemCutter, consult the `README` in `~/ultimate/releaseScripts/default/UGemCutter-linux/`. +To build Ultimate GemCutter from scratch, go to `~/ultimate/releaseScripts/default/` and run `./makeFresh.sh`. + +A pre-built binary of Goblint is available as `~/goblint/goblint`. +See `~/goblint/README.md` for information on how to run Goblint. +To build Goblint from scratch, run `make setup && make release`. + +Both GemCutter and Goblint can be invoked via the BenchExec benchmarking tool () which is installed in the VM. +For examples, see the benchmark definition files `~/witness-generation/goblint.xml` resp. `~/witness-validation/gemcutter.xml` and the scripts `~/scripts/generate-witnesses.sh` resp. `~/scripts/validate-witnesses.sh`. + +---------------------------------------------------------------------------------- + +## BENCHMARK PROGRAMS + +This artifact includes the benchmark programs on which we evaluated the verifiers. +These benchmarks are taken from the publicly available sv-benchmarks set () +and correspond to the _ConcurrencySafety-Main_ category of SV-COMP'24 (). +The benchmarks are written in C and use POSIX threads (`pthreads`) to model concurrency. + +---------------------------------------------------------------------------------- + +## EXTENDING & REUSING THIS ARTIFACT + +* **Building a modified version of the VM:** This VM was created using the `vagrant` tool (). + The `Vagrantfile` used to build the artifact, along with several other files used in the build, is included in the directory `~/artifact`. + This can be used to inspect the setup of the VM, and even build a modified version. + + Note that, to rebuild the VM, some files (e.g. scripts, evaluation results, this README) need to be extracted from the image and placed in a suitable location on your machine. + +* **Adding benchmarks:** You can easily add your own benchmarks programs written in C. + C programs should contain an empty function called `reach_error()`. Goblint and GemCutter then check that this function is never invoked. Certain (gcc) preprocessing steps may be necessary, e.g. to resolve `#include`s. See the SV-COMP benchmarks for examples (the preprocessed files typically have the extension `.i`). + + To run the evaluation on your own programs, you must edit the benchmark definition files `~/witness-generation/goblint.xml.template` resp. `~/witness-validation/gemcutter.xml.template`. + Replace the `` path specified in the task set `minimal` with your own path. + You can then simply run `~/scripts/quick-run.sh`. + +* **Adding more tools:** As described above, you can reuse the `Vagrantfile` for this artifact and extend it with whatever installation measures are necessary for an additional tool. Also note, that in order to run other tools with BenchExec, you must write a *tool info module* in python (). + + Create a new benchmark definition file for your tool (). + The existing files `~/witness-generation/goblint.xml.template` resp. `~/witness-validation/gemcutter.xml.template` can serve as an example. + + If you are planning to support generation or validation of correctness witnesses using the proposed format, take a look at the YAML schema definition for the format linked in the paper. + The schema is also available in this artifact as `~/artifact-files/correctness-witness-schema.yml`. + diff --git a/mkdocs.yml b/mkdocs.yml index a4c4238601..b55787f8da 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -41,3 +41,4 @@ nav: - "🇸 SAS '21": artifact-descriptions/sas21.md - "🇪 ESOP '23": artifact-descriptions/esop23.md - "🇻 VMCAI '24": artifact-descriptions/vmcai24.md + - "🇻 VMCAI '25": artifact-descriptions/vmcai25.md From fa3538c576e39166d0d0850772d83273afc95f48 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 12:21:03 +0200 Subject: [PATCH 544/566] Fix lists in VMACI25 artifact description --- docs/artifact-descriptions/vmcai25.md | 43 ++++++++++++++------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/artifact-descriptions/vmcai25.md b/docs/artifact-descriptions/vmcai25.md index 08b2c468b8..75374c659b 100644 --- a/docs/artifact-descriptions/vmcai25.md +++ b/docs/artifact-descriptions/vmcai25.md @@ -164,22 +164,22 @@ This script behaves similarly to the smaller variants in the previous sections. Note however: - By default the experiments require 16 GB of memory per benchmark (this configuration was used for the experiments in the paper). - To reproduce this, you will have to modify the VM's settings in VirtualBox and increase the available memory (shutdown the machine while doing so). + To reproduce this, you will have to modify the VM's settings in VirtualBox and increase the available memory (shutdown the machine while doing so). - Alternatively, you can run the experiments with a reduced memory limit. - To do so, modify the environment variable `BENCHMARK_PARAMS`. - For instance, the following allows only 4GB of memory per benchmark: + Alternatively, you can run the experiments with a reduced memory limit. + To do so, modify the environment variable `BENCHMARK_PARAMS`. + For instance, the following allows only 4GB of memory per benchmark: - BENCHMARK_PARAMS="-M 4GB" ~/scripts/full-run.sh + BENCHMARK_PARAMS="-M 4GB" ~/scripts/full-run.sh - The full evaluation for the paper required around 3 days. - In this evaluation, we used the benchexec tool to run 14 validation tasks in parallel (occupying up to 28 cores at a time). + In this evaluation, we used the benchexec tool to run 14 validation tasks in parallel (occupying up to 28 cores at a time). - By default, the provided script only runs one benchmark at a time. - If you have sufficient cores and memory available (adjust the VM settings accordingly), you can run multiple benchmarks in parallel by setting the environment variable `BENCHEXEC_THREADS`. You may also execute the experiments with a reduced timeout. - For instance, the following command runs 4 benchmarks in parallel at a time (occupying up to 8 cores), and gives each benchmark a 300s timeout and 4GB memory limit: + By default, the provided script only runs one benchmark at a time. + If you have sufficient cores and memory available (adjust the VM settings accordingly), you can run multiple benchmarks in parallel by setting the environment variable `BENCHEXEC_THREADS`. You may also execute the experiments with a reduced timeout. + For instance, the following command runs 4 benchmarks in parallel at a time (occupying up to 8 cores), and gives each benchmark a 300s timeout and 4GB memory limit: - BENCHEXEC_THREADS=4 BENCHMARK_PARAMS="-T 300s -M 4GB" ~/scripts/full-run.sh + BENCHEXEC_THREADS=4 BENCHMARK_PARAMS="-T 300s -M 4GB" ~/scripts/full-run.sh Naturally, changes to the timeout or memory are expected to affect the evaluation numbers. @@ -202,6 +202,7 @@ The table contains the following configurations : In this mode, described in the paper, GemCutter only checks if the given witness' invariants are correct, but does not prove the corresponding program correct. The summary table shows how many benchmarks were analysed and the results. + - The row `correct true` indicates tasks that were successfully verified (for `verify`), or where the witness was confirmed resp. validated (for the other configurations). - The row `correct false` indicates that a bug was found (for `verify`), resp. that witness validation failed and a witness was rejected. The latter only happens for programs that are incorrect, hence there can be no valid correctness witness and rejection is expected. @@ -266,23 +267,23 @@ The benchmarks are written in C and use POSIX threads (`pthreads`) to model conc ## EXTENDING & REUSING THIS ARTIFACT * **Building a modified version of the VM:** This VM was created using the `vagrant` tool (). - The `Vagrantfile` used to build the artifact, along with several other files used in the build, is included in the directory `~/artifact`. - This can be used to inspect the setup of the VM, and even build a modified version. + The `Vagrantfile` used to build the artifact, along with several other files used in the build, is included in the directory `~/artifact`. + This can be used to inspect the setup of the VM, and even build a modified version. - Note that, to rebuild the VM, some files (e.g. scripts, evaluation results, this README) need to be extracted from the image and placed in a suitable location on your machine. + Note that, to rebuild the VM, some files (e.g. scripts, evaluation results, this README) need to be extracted from the image and placed in a suitable location on your machine. * **Adding benchmarks:** You can easily add your own benchmarks programs written in C. - C programs should contain an empty function called `reach_error()`. Goblint and GemCutter then check that this function is never invoked. Certain (gcc) preprocessing steps may be necessary, e.g. to resolve `#include`s. See the SV-COMP benchmarks for examples (the preprocessed files typically have the extension `.i`). + C programs should contain an empty function called `reach_error()`. Goblint and GemCutter then check that this function is never invoked. Certain (gcc) preprocessing steps may be necessary, e.g. to resolve `#include`s. See the SV-COMP benchmarks for examples (the preprocessed files typically have the extension `.i`). - To run the evaluation on your own programs, you must edit the benchmark definition files `~/witness-generation/goblint.xml.template` resp. `~/witness-validation/gemcutter.xml.template`. - Replace the `` path specified in the task set `minimal` with your own path. - You can then simply run `~/scripts/quick-run.sh`. + To run the evaluation on your own programs, you must edit the benchmark definition files `~/witness-generation/goblint.xml.template` resp. `~/witness-validation/gemcutter.xml.template`. + Replace the `` path specified in the task set `minimal` with your own path. + You can then simply run `~/scripts/quick-run.sh`. * **Adding more tools:** As described above, you can reuse the `Vagrantfile` for this artifact and extend it with whatever installation measures are necessary for an additional tool. Also note, that in order to run other tools with BenchExec, you must write a *tool info module* in python (). - Create a new benchmark definition file for your tool (). - The existing files `~/witness-generation/goblint.xml.template` resp. `~/witness-validation/gemcutter.xml.template` can serve as an example. + Create a new benchmark definition file for your tool (). + The existing files `~/witness-generation/goblint.xml.template` resp. `~/witness-validation/gemcutter.xml.template` can serve as an example. - If you are planning to support generation or validation of correctness witnesses using the proposed format, take a look at the YAML schema definition for the format linked in the paper. - The schema is also available in this artifact as `~/artifact-files/correctness-witness-schema.yml`. + If you are planning to support generation or validation of correctness witnesses using the proposed format, take a look at the YAML schema definition for the format linked in the paper. + The schema is also available in this artifact as `~/artifact-files/correctness-witness-schema.yml`. From 9fecf6ee40761503ee63b1072db0937e23dbdc69 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 12:23:28 +0200 Subject: [PATCH 545/566] Add Goblint code references to VMCAI25 artifact description --- docs/artifact-descriptions/vmcai25.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/artifact-descriptions/vmcai25.md b/docs/artifact-descriptions/vmcai25.md index 75374c659b..b4eec295ac 100644 --- a/docs/artifact-descriptions/vmcai25.md +++ b/docs/artifact-descriptions/vmcai25.md @@ -236,6 +236,16 @@ More recent versions of Ultimate can be found at ) is developed by TU Munich and University of Tartu. The source code for Goblint at the time of evaluation can be found in this artifact in the `~/goblint` directory. +The code for this paper is the following: + +1. `src/witness/witnessGhostVar.ml` and `src/witness/witnessGhost.ml` define the data types for ghost variables. +2. `src/analyses/mutexGhosts.ml` defines the analysis which determines the ghost variables for a specific program and their updates. +3. `src/analyses/basePriv.ml` lines 342-365 define the invariants with mutex ghost variables from non-relational _mutex-meet_ analysis. +4. `src/analyses/apron/relationPriv.apron.ml` lines 717-750 define the invariants with mutex ghost variables from relational _mutex-meet_ analysis. +5. `src/analyses/base.ml` lines 1269-1289 and `src/analyses/apron/relationAnalysis.apron.ml` lines 637-644 define the wrapping of the invariants with multithreaded mode ghost variables. +6. `src/analyses/basePriv.ml` lines 882-909 define the invariants with mutex ghost variables from (non-relational) _protection_ analysis. +7. `src/witness/yamlWitness.ml` lines 398-421 and 589-621 define the YAML output of ghost variables, their updates and the invariants. + More recent versions of Goblint can be found at . ---------------------------------------------------------------------------------- From 5c9b5fb5e67572f5b8dd4a0424558d469b7bcf2f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 12:28:46 +0200 Subject: [PATCH 546/566] Rewrite VMCAI25 artifact description intro --- docs/artifact-descriptions/vmcai25.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/artifact-descriptions/vmcai25.md b/docs/artifact-descriptions/vmcai25.md index b4eec295ac..ac9312caa8 100644 --- a/docs/artifact-descriptions/vmcai25.md +++ b/docs/artifact-descriptions/vmcai25.md @@ -1,4 +1,11 @@ -# Artifact for VMCAI'2025 Paper "Correctness Witnesses for Concurrent Programs: Bridging the Semantic Divide with Ghosts" +# VMCAI '25 Artifact Description +## Correctness Witnesses for Concurrent Programs: Bridging the Semantic Divide with Ghosts + +This is the artifact description for our [VMCAI '25 paper "Correctness Witnesses for Concurrent Programs: Bridging the Semantic Divide with Ghosts"](https://doi.org/10.48550/arXiv.2411.16612). +The artifact is available on [Zenodo](https://doi.org/10.5281/zenodo.13863579). + +**The description here is provided for convenience and not maintained.** +The artifact contains [Goblint at `vmcai25` git tag](https://github.com/goblint/analyzer/releases/tag/vmcai25). ------------------------------------------------------------------------------- From 55bfe9631ab58fdf0275b8560eb222eba0e2f361 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 12:31:55 +0200 Subject: [PATCH 547/566] Remove all-caps headings in VMCAI25 artifact description --- docs/artifact-descriptions/vmcai25.md | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/artifact-descriptions/vmcai25.md b/docs/artifact-descriptions/vmcai25.md index ac9312caa8..9f2de1c511 100644 --- a/docs/artifact-descriptions/vmcai25.md +++ b/docs/artifact-descriptions/vmcai25.md @@ -9,20 +9,20 @@ The artifact contains [Goblint at `vmcai25` git tag](https://github.com/goblint/ ------------------------------------------------------------------------------- -## OVERVIEW +## Overview This artifact contains the following components: - EVALUATION RESULTS :: The evaluation results, and overview tables (HTML) + Evaluation Results :: The evaluation results, and overview tables (HTML) generated from the raw data. - SOURCE CODE :: Source code for Goblint and Ultimate GemCutter, + Source Code :: Source code for Goblint and Ultimate GemCutter, the verification tools used in the experiments for the paper. - VERIFIER BINARIES :: Binaries for Goblint and Ultimate GemCutter. - BENCHMARK PROGRAMS :: Benchmarks used for evaluation of the verifiers. - BENCHMARK WITNESSES :: The witnesses generated by Goblint, as used in the + Verifier Binaries :: Binaries for Goblint and Ultimate GemCutter. + Benchmark Programs :: Benchmarks used for evaluation of the verifiers. + Benchmark Witnesses :: The witnesses generated by Goblint, as used in the experiments. - BENCHEXEC TOOL :: The BenchExec benchmarking tool can be used + BenchExec Tool :: The BenchExec benchmarking tool can be used to replicate our results on these benchmarks. The next section gives instructions on how to setup and quickly get an overview of the artifact. @@ -31,7 +31,7 @@ The final section gives information on how to reuse this artifact. ------------------------------------------------------------------------------- -## GETTING STARTED +## Getting Started ### 1. Setup @@ -67,7 +67,7 @@ The times for the Table 2 are given first in seconds and then pretty-printed as For a more detailed inspection and visualization of the data, take a look at the generated HTML report. Just open the file `~/witness-validation/paper-evaluation/results.2024-09-24_22-00-48.table.html` in firefox. -For detailed information, see the "EVALUATION RESULTS" section below. +For detailed information, see the "Evaluation Results" section below. ### 3. Quick Test of the Benchmark Setup @@ -192,7 +192,7 @@ Naturally, changes to the timeout or memory are expected to affect the evaluatio ---------------------------------------------------------------------------------- -## EVALUATION RESULTS +## Evaluation Results The evaluation results that form the basis for the experimental data in the paper can be found in the directory `~/witness-validation/paper-evaluation/`. The witnesses generated by Goblint that formed the basis for this evaluation can be found in `~/witness-generation/paper-evaluation/`. @@ -228,7 +228,7 @@ As described above (in section _2. Inspect the evaluation results_), the artifac ---------------------------------------------------------------------------------- -## SOURCE CODE +## Source Code ### GemCutter @@ -257,7 +257,7 @@ More recent versions of Goblint can be found at . ---------------------------------------------------------------------------------- -## VERIFIER BINARIES +## Verifier Binaries A pre-built binary of GemCutter can be found in `~/ultimate/releaseScripts/default/UGemCutter-linux/`. For information on how to execute GemCutter, consult the `README` in `~/ultimate/releaseScripts/default/UGemCutter-linux/`. @@ -272,7 +272,7 @@ For examples, see the benchmark definition files `~/witness-generation/goblint.x ---------------------------------------------------------------------------------- -## BENCHMARK PROGRAMS +## Benchmark Programs This artifact includes the benchmark programs on which we evaluated the verifiers. These benchmarks are taken from the publicly available sv-benchmarks set () @@ -281,7 +281,7 @@ The benchmarks are written in C and use POSIX threads (`pthreads`) to model conc ---------------------------------------------------------------------------------- -## EXTENDING & REUSING THIS ARTIFACT +## Extending & Reusing This Artifact * **Building a modified version of the VM:** This VM was created using the `vagrant` tool (). The `Vagrantfile` used to build the artifact, along with several other files used in the build, is included in the directory `~/artifact`. From fcc2d02afcd1bb5c4e8c140cc10f5569cc18f569 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 12:32:37 +0200 Subject: [PATCH 548/566] Remove horizontal rules in VMCAI25 artifact description --- docs/artifact-descriptions/vmcai25.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/artifact-descriptions/vmcai25.md b/docs/artifact-descriptions/vmcai25.md index 9f2de1c511..d53faa310a 100644 --- a/docs/artifact-descriptions/vmcai25.md +++ b/docs/artifact-descriptions/vmcai25.md @@ -7,7 +7,6 @@ The artifact is available on [Zenodo](https://doi.org/10.5281/zenodo.13863579). **The description here is provided for convenience and not maintained.** The artifact contains [Goblint at `vmcai25` git tag](https://github.com/goblint/analyzer/releases/tag/vmcai25). -------------------------------------------------------------------------------- ## Overview @@ -29,7 +28,6 @@ The next section gives instructions on how to setup and quickly get an overview The subsequent sections then describe each of these components in detail. The final section gives information on how to reuse this artifact. -------------------------------------------------------------------------------- ## Getting Started @@ -190,7 +188,6 @@ Note however: Naturally, changes to the timeout or memory are expected to affect the evaluation numbers. ----------------------------------------------------------------------------------- ## Evaluation Results @@ -226,8 +223,6 @@ Clicking on a status shows the complete GemCutter log for the benchmark run. As described above (in section _2. Inspect the evaluation results_), the artifact provides python scripts to directly extract the data shown in the paper from the benchmark results. ----------------------------------------------------------------------------------- - ## Source Code ### GemCutter @@ -255,7 +250,6 @@ The code for this paper is the following: More recent versions of Goblint can be found at . ----------------------------------------------------------------------------------- ## Verifier Binaries @@ -270,7 +264,6 @@ To build Goblint from scratch, run `make setup && make release`. Both GemCutter and Goblint can be invoked via the BenchExec benchmarking tool () which is installed in the VM. For examples, see the benchmark definition files `~/witness-generation/goblint.xml` resp. `~/witness-validation/gemcutter.xml` and the scripts `~/scripts/generate-witnesses.sh` resp. `~/scripts/validate-witnesses.sh`. ----------------------------------------------------------------------------------- ## Benchmark Programs @@ -279,7 +272,6 @@ These benchmarks are taken from the publicly available sv-benchmarks set (). The benchmarks are written in C and use POSIX threads (`pthreads`) to model concurrency. ----------------------------------------------------------------------------------- ## Extending & Reusing This Artifact From 73295e4bc72ab75a00bcb9406c054d90dde742f3 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 12:37:32 +0200 Subject: [PATCH 549/566] Fix and add links in VMCAI25 artifact description --- docs/artifact-descriptions/vmcai25.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/artifact-descriptions/vmcai25.md b/docs/artifact-descriptions/vmcai25.md index d53faa310a..8b0cabbbaf 100644 --- a/docs/artifact-descriptions/vmcai25.md +++ b/docs/artifact-descriptions/vmcai25.md @@ -36,7 +36,7 @@ The final section gives information on how to reuse this artifact. The artifact is a virtual machine (VM). Follow these steps to set it up: * If you have not done so already, install VirtualBox. - (https://www.virtualbox.org/wiki/Downloads) + () * Download the artifact. * Import the artifact via the VirtualBox UI (`File > Import Appliance`) or by running `VBoxManage import ghost-witnesses.ova`. @@ -65,7 +65,7 @@ The times for the Table 2 are given first in seconds and then pretty-printed as For a more detailed inspection and visualization of the data, take a look at the generated HTML report. Just open the file `~/witness-validation/paper-evaluation/results.2024-09-24_22-00-48.table.html` in firefox. -For detailed information, see the "Evaluation Results" section below. +For detailed information, see the ["Evaluation Results" section](#evaluation-results) below. ### 3. Quick Test of the Benchmark Setup @@ -220,7 +220,7 @@ Clicking on a status shows the complete GemCutter log for the benchmark run. > **Note:** If you are trying to view logs for individual runs through the HTML table (by clicking on the evaluation result `true` or `false`), you may encounter a warning because browsers block access to local files. Follow the instructions in the message to enable log viewing. -As described above (in section _2. Inspect the evaluation results_), the artifact provides python scripts to directly extract the data shown in the paper from the benchmark results. +As described above (in [section _2. Inspect the evaluation results_](#2-inspect-the-evaluation-results)), the artifact provides python scripts to directly extract the data shown in the paper from the benchmark results. ## Source Code From 2c90552b8e03581f4d9d83e2a90fbf0de2e25419 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 12:38:07 +0200 Subject: [PATCH 550/566] Fix duplicated path in VMCAI25 artifact description --- docs/artifact-descriptions/vmcai25.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/artifact-descriptions/vmcai25.md b/docs/artifact-descriptions/vmcai25.md index 8b0cabbbaf..282be65ff0 100644 --- a/docs/artifact-descriptions/vmcai25.md +++ b/docs/artifact-descriptions/vmcai25.md @@ -195,7 +195,7 @@ The evaluation results that form the basis for the experimental data in the pape The witnesses generated by Goblint that formed the basis for this evaluation can be found in `~/witness-generation/paper-evaluation/`. See below for detailed info to reproduce the tables and figures of the paper. -The file `~/witness-validation/paper-evaluation/~/witness-validation/paper-evaluation/` contains an HTML overview page generated by the BenchExec benchmarking tool, which displays individual results, quantile and scatter plots. Through the filtering sidebar (top right corner), detailed analyses can be made. +The file `~/witness-validation/paper-evaluation/` contains an HTML overview page generated by the BenchExec benchmarking tool, which displays individual results, quantile and scatter plots. Through the filtering sidebar (top right corner), detailed analyses can be made. The table contains the following configurations : From 1e88c86a1cd795cdde2a76166d5323e0a9e51f91 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 15:53:24 +0200 Subject: [PATCH 551/566] Add potential NOTIMEOUT to 06-symbeq/34-var_eq-exponential-context --- tests/regression/06-symbeq/34-var_eq-exponential-context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/06-symbeq/34-var_eq-exponential-context.c b/tests/regression/06-symbeq/34-var_eq-exponential-context.c index bc478374f0..8b9b76a89e 100644 --- a/tests/regression/06-symbeq/34-var_eq-exponential-context.c +++ b/tests/regression/06-symbeq/34-var_eq-exponential-context.c @@ -1,5 +1,5 @@ // SKIP PARAM: --set ana.activated[+] var_eq - +// NOTIMEOUT? void level0(int *p) { } From 55e497387b7dc07967a494bb7f3c0a40e3128973 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 15:57:21 +0200 Subject: [PATCH 552/566] Renumber 06-symbeq/41-var_eq_multithread to fix duplicate ID --- .../{41-var_eq_multithread.c => 47-var_eq_multithread.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/regression/06-symbeq/{41-var_eq_multithread.c => 47-var_eq_multithread.c} (100%) diff --git a/tests/regression/06-symbeq/41-var_eq_multithread.c b/tests/regression/06-symbeq/47-var_eq_multithread.c similarity index 100% rename from tests/regression/06-symbeq/41-var_eq_multithread.c rename to tests/regression/06-symbeq/47-var_eq_multithread.c From c24821f83adacfa211fb3082192046088a22877c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 16:03:10 +0200 Subject: [PATCH 553/566] Fix double-locking in symbolic regression tests --- tests/regression/06-symbeq/20-mult_accs_nr.c | 2 +- tests/regression/06-symbeq/21-mult_accs_rc.c | 2 +- tests/regression/06-symbeq/21-mult_accs_rc.t | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/regression/06-symbeq/20-mult_accs_nr.c b/tests/regression/06-symbeq/20-mult_accs_nr.c index 7d66e3f5d2..50c8f19a62 100644 --- a/tests/regression/06-symbeq/20-mult_accs_nr.c +++ b/tests/regression/06-symbeq/20-mult_accs_nr.c @@ -15,7 +15,7 @@ void *t_fun(void *arg) { pthread_mutex_lock(&s->mutex); s->data = 5; // NORACE s->lore = 6; // NORACE - pthread_mutex_lock(&s->mutex); + pthread_mutex_unlock(&s->mutex); return NULL; } diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.c b/tests/regression/06-symbeq/21-mult_accs_rc.c index 62550fab55..4acadb4a58 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.c +++ b/tests/regression/06-symbeq/21-mult_accs_rc.c @@ -14,7 +14,7 @@ void *t_fun(void *arg) { pthread_mutex_lock(&s->mutex); s = get_s(); s->data = 5; // RACE! - pthread_mutex_lock(&s->mutex); + pthread_mutex_unlock(&s->mutex); return NULL; } diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.t b/tests/regression/06-symbeq/21-mult_accs_rc.t index ca2e219b05..2eacd0382e 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.t +++ b/tests/regression/06-symbeq/21-mult_accs_rc.t @@ -3,7 +3,7 @@ Disable info messages because race summary contains (safe) memory location count $ goblint --enable warn.deterministic --disable warn.info --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" 21-mult_accs_rc.c 2>&1 | tee default-output-1.txt [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:14:3-14:32) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:16:3-16:14) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:17:3-17:32) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:17:3-17:34) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:28:3-28:16) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:29:3-29:15) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:34:3-34:9) @@ -11,7 +11,8 @@ Disable info messages because race summary contains (safe) memory location count write with thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) write with [symblock:{p-lock:*.mutex}, mhp:{created={[main, t_fun@21-mult_accs_rc.c:31:3-31:37]}}, thread:[main]] (conf. 100) (exp: & *d) (21-mult_accs_rc.c:34:3-34:9) [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:14:3-14:32) - [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:17:3-17:32) + [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:17:3-17:34) + [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:17:3-17:34) [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:33:3-33:24) [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) @@ -23,14 +24,15 @@ Disable info messages because race summary contains (safe) memory location count $ goblint --enable warn.deterministic --disable warn.info --disable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --enable allglobs 21-mult_accs_rc.c 2>&1 | tee default-output-2.txt [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:14:3-14:32) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:16:3-16:14) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:17:3-17:32) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:17:3-17:34) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:28:3-28:16) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:29:3-29:15) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:34:3-34:9) [Success][Race] Memory location (struct s).data (safe): write with thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:14:3-14:32) - [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:17:3-17:32) + [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:17:3-17:34) + [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:17:3-17:34) [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:33:3-33:24) [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) From 87ce3a5788ff89ffb931414ae29a6ca26442f6f5 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 10 Dec 2024 16:05:11 +0200 Subject: [PATCH 554/566] Comment reversal of PartitionDomain.SetSet --- src/domain/partitionDomain.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/domain/partitionDomain.ml b/src/domain/partitionDomain.ml index 316f4fb705..e97946e463 100644 --- a/src/domain/partitionDomain.ml +++ b/src/domain/partitionDomain.ml @@ -106,6 +106,9 @@ struct let show _ = "Partitions" + (* Top and bottom are reversed: + Bottom will be All (equations), i.e. contradiction, + Top will be empty set, i.e. no equations. *) let top = E.bot let bot = E.top let is_top = E.is_bot From 4a9b52f57702586b1e876cde83bdedac3119b6f6 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 16 Dec 2024 10:56:50 +0200 Subject: [PATCH 555/566] Fix invalid widen call in slr3 for globals --- src/solver/sLR.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver/sLR.ml b/src/solver/sLR.ml index 69d415307a..299bbbce52 100644 --- a/src/solver/sLR.ml +++ b/src/solver/sLR.ml @@ -66,7 +66,7 @@ module SLR3 = if tracing then trace "sol" "Contrib:%a" S.Dom.pretty tmp; let tmp = if wpx then - if HM.mem globals x then S.Dom.widen old tmp (* TODO: no join in second argument, can call widen incorrectly? *) + if HM.mem globals x then S.Dom.widen old (S.Dom.join old tmp) else box old tmp else tmp in From b0243f91c6a3013d797b7a6cde8c59a605cfe0c4 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 16 Dec 2024 11:00:43 +0200 Subject: [PATCH 556/566] Disable broken two solver --- src/solver/sLR.ml | 2 +- tests/regression/00-sanity/01-assert.t | 115 ------------------------- 2 files changed, 1 insertion(+), 116 deletions(-) diff --git a/src/solver/sLR.ml b/src/solver/sLR.ml index 299bbbce52..03ba9307aa 100644 --- a/src/solver/sLR.ml +++ b/src/solver/sLR.ml @@ -527,7 +527,7 @@ let _ = Selector.add_solver ("widen2", (module PostSolver.EqIncrSolverFromEqSolver (W2))); Selector.add_solver ("widen3", (module PostSolver.EqIncrSolverFromEqSolver (W3))); let module S2 = TwoPhased (struct let ver = 1 end) in - Selector.add_solver ("two", (module PostSolver.EqIncrSolverFromEqSolver (S2))); (* TODO: broken even on 00-sanity/01-assert *) + (* Selector.add_solver ("two", (module PostSolver.EqIncrSolverFromEqSolver (S2))); (* TODO: broken even on 00-sanity/01-assert *) *) let module S1 = Make (struct let ver = 1 end) in Selector.add_solver ("new", (module PostSolver.EqIncrSolverFromEqSolver (S1))); Selector.add_solver ("slr+", (module PostSolver.EqIncrSolverFromEqSolver (S1))) diff --git a/tests/regression/00-sanity/01-assert.t b/tests/regression/00-sanity/01-assert.t index cd8c4c06f8..9b3b55f530 100644 --- a/tests/regression/00-sanity/01-assert.t +++ b/tests/regression/00-sanity/01-assert.t @@ -139,121 +139,6 @@ Test SLR solvers: dead: 2 total lines: 9 - $ goblint --enable warn.deterministic --set solver two 01-assert.c - [Error] Fixpoint not reached at L:entry state of main (299) on 01-assert.c:4:1-15:1 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), - mallocWrapper:(wrapper call:Unknown node, unique calls:{}), - base:({ - }, {}, {}, {}), - threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), - threadflag:Singlethreaded, - threadreturn:true, - escape:{}, - mutexEvents:(), - access:(), - mutex:(lockset:{}, multiplicity:{}), - race:(), - mhp:(), - assert:(), - pthreadMutexType:()], map:{})} - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), - mallocWrapper:(wrapper call:Unknown node, unique calls:{}), - base:({ - }, {}, {}, {}), - threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), - threadflag:Singlethreaded, - threadreturn:true, - escape:{}, - mutexEvents:(), - access:(), - mutex:(lockset:{}, multiplicity:{}), - race:(), - mhp:(), - assert:(), - pthreadMutexType:()], map:{})} instead of bot - - [Error] Fixpoint not reached at L:node 1 "success = 1;" on 01-assert.c:5:7-5:18 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot - - [Error] Fixpoint not reached at L:node 2 "silence = 1;" on 01-assert.c:6:7-6:18 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot - - [Error] Fixpoint not reached at L:node 3 "fail = 0;" on 01-assert.c:7:7-7:15 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot - - [Error] Fixpoint not reached at L:node 4 "__goblint_assert(success);" on 01-assert.c:10:3-10:28 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot - - [Error] Fixpoint not reached at L:node 5 "__goblint_assert(unknown == 4);" on 01-assert.c:11:3-11:33 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot - - [Error] Fixpoint not reached at L:node 6 "__goblint_assert(fail);" on 01-assert.c:12:3-12:25 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot - - [Error] Fixpoint not reached at L:node 7 "return (0);" on 01-assert.c:13:10-13:11 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot - - [Error] Fixpoint not reached at L:node 9 "__goblint_assert(silence);" on 01-assert.c:14:3-14:28 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot - - [Error] Fixpoint not reached at L:node -299 "return;" on 01-assert.c:15:1-15:1 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot - - [Error] Fixpoint not reached at L:call of main (299) on 01-assert.c:4:1-15:1 - Solver computed: - bot - Right-Hand-Side: - HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code - Difference: HConsed lifted PathSensitive (ProjectiveSet (MCP.D * map)):Dead code instead of bot - - [Warning][Deadcode] Function 'main' does not return - [Warning][Deadcode] Function 'main' is uncalled: 8 LLoC (01-assert.c:4:1-15:1) - [Info][Deadcode] Logical lines of code (LLoC) summary: - live: 0 - dead: 8 (8 in uncalled functions) - total lines: 8 - [Error][Unsound] Fixpoint not reached - [3] - $ goblint --enable warn.deterministic --set solver new 01-assert.c [Error][Assert] Assertion "fail" will fail. (01-assert.c:12:3-12:25) [Warning][Assert] Assertion "unknown == 4" is unknown. (01-assert.c:11:3-11:33) From ba396747badffe0a703136d1687cbaa420dcbe50 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 16 Dec 2024 17:40:49 +0200 Subject: [PATCH 557/566] Add mine-W-noinit test for resetting W in threadenter --- .../13-privatized/96-mine-W-threadenter.c | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/regression/13-privatized/96-mine-W-threadenter.c diff --git a/tests/regression/13-privatized/96-mine-W-threadenter.c b/tests/regression/13-privatized/96-mine-W-threadenter.c new file mode 100644 index 0000000000..424d45c52b --- /dev/null +++ b/tests/regression/13-privatized/96-mine-W-threadenter.c @@ -0,0 +1,32 @@ +// PARAM: --set ana.base.privatization mine-W-noinit --enable ana.int.enums +#include +#include + +int g; +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + return NULL; +} + +void *t_fun2(void *arg) { + pthread_mutex_lock(&A); + pthread_mutex_unlock(&A); // spuriously publishes g = 8 + return NULL; +} + +int main() { + pthread_t id, id2; + pthread_create(&id, NULL, t_fun, NULL); // enter multithreaded + + pthread_mutex_lock(&A); + g = 8; + pthread_create(&id2, NULL, t_fun2, NULL); // passes g = 8 and W: A -> {g} to t_fun2 + g = 0; + pthread_mutex_unlock(&A); + + pthread_mutex_lock(&A); + __goblint_check(g == 0); // TODO + pthread_mutex_unlock(&A); + return 0; +} From be4d3de02fba0cc16bfc8cc3072893b0d3516d6e Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 16 Dec 2024 17:42:49 +0200 Subject: [PATCH 558/566] Fix mine-W-noinit threadenter to reset W --- src/analyses/basePriv.ml | 6 +++--- tests/regression/13-privatized/96-mine-W-threadenter.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 3afd758daa..792f084e05 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1255,11 +1255,11 @@ struct else st - let threadenter = + let threadenter ask st = if Param.side_effect_global_init then - startstate_threadenter startstate + startstate_threadenter startstate ask st else - old_threadenter + {(old_threadenter ask st) with priv = W.empty ()} end module LockCenteredD = diff --git a/tests/regression/13-privatized/96-mine-W-threadenter.c b/tests/regression/13-privatized/96-mine-W-threadenter.c index 424d45c52b..ec9903b653 100644 --- a/tests/regression/13-privatized/96-mine-W-threadenter.c +++ b/tests/regression/13-privatized/96-mine-W-threadenter.c @@ -11,7 +11,7 @@ void *t_fun(void *arg) { void *t_fun2(void *arg) { pthread_mutex_lock(&A); - pthread_mutex_unlock(&A); // spuriously publishes g = 8 + pthread_mutex_unlock(&A); // used to spuriously publish g = 8 return NULL; } @@ -21,12 +21,12 @@ int main() { pthread_mutex_lock(&A); g = 8; - pthread_create(&id2, NULL, t_fun2, NULL); // passes g = 8 and W: A -> {g} to t_fun2 + pthread_create(&id2, NULL, t_fun2, NULL); // used to pass g = 8 and W: A -> {g} to t_fun2 g = 0; pthread_mutex_unlock(&A); pthread_mutex_lock(&A); - __goblint_check(g == 0); // TODO + __goblint_check(g == 0); pthread_mutex_unlock(&A); return 0; } From 0c155e68607fede6fab17704a9a7aee38df5408e Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 18 Dec 2024 15:13:06 +0100 Subject: [PATCH 559/566] Rename ctx -> man --- src/analyses/abortUnless.ml | 34 +- src/analyses/accessAnalysis.ml | 94 +-- src/analyses/activeLongjmp.ml | 14 +- src/analyses/activeSetjmp.ml | 18 +- src/analyses/apron/relationAnalysis.apron.ml | 230 ++--- src/analyses/assert.ml | 20 +- src/analyses/base.ml | 838 +++++++++---------- src/analyses/baseInvariant.ml | 68 +- src/analyses/basePriv.ml | 4 +- src/analyses/callstring.ml | 16 +- src/analyses/condVars.ml | 58 +- src/analyses/deadlock.ml | 26 +- src/analyses/expRelation.ml | 2 +- src/analyses/expsplit.ml | 76 +- src/analyses/extractPthread.ml | 148 ++-- src/analyses/locksetAnalysis.ml | 38 +- src/analyses/loopTermination.ml | 20 +- src/analyses/loopfreeCallstring.ml | 2 +- src/analyses/mCP.ml | 400 ++++----- src/analyses/mCPRegistry.ml | 4 +- src/analyses/mHPAnalysis.ml | 2 +- src/analyses/mallocFresh.ml | 32 +- src/analyses/malloc_null.ml | 80 +- src/analyses/mayLocks.ml | 38 +- src/analyses/memLeak.ml | 110 +-- src/analyses/memOutOfBounds.ml | 146 ++-- src/analyses/modifiedSinceSetjmp.ml | 34 +- src/analyses/mutexAnalysis.ml | 76 +- src/analyses/mutexEventsAnalysis.ml | 30 +- src/analyses/mutexGhosts.ml | 56 +- src/analyses/mutexTypeAnalysis.ml | 34 +- src/analyses/poisonVariables.ml | 54 +- src/analyses/pthreadSignals.ml | 28 +- src/analyses/ptranalAnalysis.ml | 2 +- src/analyses/raceAnalysis.ml | 64 +- src/analyses/region.ml | 86 +- src/analyses/stackTrace.ml | 22 +- src/analyses/symbLocks.ml | 46 +- src/analyses/taintPartialContexts.ml | 52 +- src/analyses/threadAnalysis.ml | 66 +- src/analyses/threadEscape.ml | 92 +- src/analyses/threadFlag.ml | 36 +- src/analyses/threadId.ml | 56 +- src/analyses/threadJoins.ml | 58 +- src/analyses/threadReturn.ml | 16 +- src/analyses/tmpSpecial.ml | 26 +- src/analyses/tutorials/constants.ml | 36 +- src/analyses/tutorials/signs.ml | 8 +- src/analyses/tutorials/taint.ml | 38 +- src/analyses/tutorials/unitAnalysis.ml | 34 +- src/analyses/unassumeAnalysis.ml | 54 +- src/analyses/uninit.ml | 54 +- src/analyses/useAfterFree.ml | 106 +-- src/analyses/varEq.ml | 86 +- src/analyses/vla.ml | 22 +- src/analyses/wrapperFunctionAnalysis.ml | 46 +- src/cdomains/apron/sharedFunctions.apron.ml | 2 +- src/domains/events.ml | 2 +- src/domains/queries.ml | 6 +- src/framework/analyses.ml | 110 ++- src/framework/constraints.ml | 186 ++-- src/framework/control.ml | 42 +- src/framework/resultQuery.ml | 32 +- src/lifters/contextGasLifter.ml | 64 +- src/lifters/longjmpLifter.ml | 162 ++-- src/lifters/recursionTermLifter.ml | 70 +- src/lifters/specLifters.ml | 508 +++++------ src/lifters/wideningTokenLifter.ml | 64 +- src/witness/observerAnalysis.ml | 44 +- src/witness/witnessConstraints.ml | 148 ++-- src/witness/z3/violationZ3.z3.ml | 56 +- 71 files changed, 2711 insertions(+), 2721 deletions(-) diff --git a/src/analyses/abortUnless.ml b/src/analyses/abortUnless.ml index 34d5b1a89b..f523b21970 100644 --- a/src/analyses/abortUnless.ml +++ b/src/analyses/abortUnless.ml @@ -13,61 +13,61 @@ struct module D = BoolDomain.MustBool module C = Printable.Unit - let context ctx _ _ = () + let context man _ _ = () let startcontext () = () (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = + let assign man (lval:lval) (rval:exp) : D.t = false - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local + let branch man (exp:exp) (tv:bool) : D.t = + man.local - let body ctx (f:fundec) : D.t = - ctx.local + let body man (f:fundec) : D.t = + man.local - let return ctx (exp:exp option) (f:fundec) : D.t = - if ctx.local then + let return man (exp:exp option) (f:fundec) : D.t = + if man.local then match f.sformals with | [arg] when isIntegralType arg.vtype -> - (match ctx.ask (EvalInt (Lval (Var arg, NoOffset))) with + (match man.ask (EvalInt (Lval (Var arg, NoOffset))) with | v when Queries.ID.is_bot v -> false | v -> match Queries.ID.to_bool v with | Some b -> b | None -> false) | _ -> - (* should not happen, ctx.local should always be false in this case *) + (* should not happen, man.local should always be false in this case *) false else false - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = let candidate = match f.sformals with | [arg] when isIntegralType arg.vtype -> true | _ -> false in [false, candidate] - let combine_env ctx lval fexp f args fc au f_ask = + let combine_env man lval fexp f args fc au f_ask = if au then ( (* Assert before combine_assign, so if variables in `arg` are assigned to, asserting doesn't unsoundly yield bot *) (* See test 62/03 *) match args with - | [arg] -> ctx.emit (Events.Assert arg) + | [arg] -> man.emit (Events.Assert arg) | _ -> () ); false - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = false - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = false let startstate v = false - let threadenter ctx ~multiple lval f args = [false] - let threadspawn ctx ~multiple lval f args fctx = false + let threadenter man ~multiple lval f args = [false] + let threadspawn man ~multiple lval f args fman = false let exitstate v = false end diff --git a/src/analyses/accessAnalysis.ml b/src/analyses/accessAnalysis.ml index bf1892fdf0..55d79a1131 100644 --- a/src/analyses/accessAnalysis.ml +++ b/src/analyses/accessAnalysis.ml @@ -31,23 +31,23 @@ struct let activated = get_string_list "ana.activated" in emit_single_threaded := List.mem (ModifiedSinceSetjmp.Spec.name ()) activated || List.mem (PoisonVariables.Spec.name ()) activated - let do_access (ctx: (D.t, G.t, C.t, V.t) ctx) (kind:AccessKind.t) (reach:bool) (e:exp) = + let do_access (man: (D.t, G.t, C.t, V.t) man) (kind:AccessKind.t) (reach:bool) (e:exp) = if M.tracing then M.trace "access" "do_access %a %a %B" d_exp e AccessKind.pretty kind reach; let reach_or_mpt: _ Queries.t = if reach then ReachableFrom e else MayPointTo e in - let ad = ctx.ask reach_or_mpt in - ctx.emit (Access {exp=e; ad; kind; reach}) + let ad = man.ask reach_or_mpt in + man.emit (Access {exp=e; ad; kind; reach}) (** Three access levels: + [deref=false], [reach=false] - Access [exp] without dereferencing, used for all normal reads and all function call arguments. + [deref=true], [reach=false] - Access [exp] by dereferencing once (may-point-to), used for lval writes and shallow special accesses. + [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 = + let access_one_top ?(force=false) ?(deref=false) man (kind: AccessKind.t) reach exp = if M.tracing then M.traceli "access" "access_one_top %a (kind = %a, reach = %B, deref = %B)" CilType.Exp.pretty exp AccessKind.pretty kind reach deref; - if force || !collect_local || !emit_single_threaded || ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) then ( + if force || !collect_local || !emit_single_threaded || ThreadFlag.has_ever_been_multi (Analyses.ask_of_man man) then ( if deref && Cil.isPointerType (Cilfacade.typeOf exp) then (* avoid dereferencing integers to unknown pointers, which cause many spurious type-based accesses *) - do_access ctx kind reach exp; + do_access man kind reach exp; if M.tracing then M.tracei "access" "distribute_access_exp"; - Access.distribute_access_exp (do_access ctx Read false) exp; + Access.distribute_access_exp (do_access man Read false) exp; if M.tracing then M.traceu "access" "distribute_access_exp"; ); if M.tracing then M.traceu "access" "access_one_top" @@ -55,88 +55,88 @@ struct (** We just lift start state, global and dependency functions: *) let startstate v = () - let threadenter ctx ~multiple lval f args = [()] + let threadenter man ~multiple lval f args = [()] let exitstate v = () - let context ctx fd d = () + let context man fd d = () (** Transfer functions: *) - let vdecl ctx v = - access_one_top ctx Read false (SizeOf v.vtype); - ctx.local + let vdecl man v = + access_one_top man Read false (SizeOf v.vtype); + man.local - let assign ctx lval rval : D.t = + let assign man lval rval : D.t = (* ignore global inits *) - if !AnalysisState.global_initialization then ctx.local else begin - access_one_top ~deref:true ctx Write false (AddrOf lval); - access_one_top ctx Read false rval; - ctx.local + if !AnalysisState.global_initialization then man.local else begin + access_one_top ~deref:true man Write false (AddrOf lval); + access_one_top man Read false rval; + man.local end - let branch ctx exp tv : D.t = - access_one_top ctx Read false exp; - ctx.local + let branch man exp tv : D.t = + access_one_top man Read false exp; + man.local - let return ctx exp fundec : D.t = + let return man exp fundec : D.t = begin match exp with - | Some exp -> access_one_top ctx Read false exp + | Some exp -> access_one_top man Read false exp | None -> () end; - ctx.local + man.local - let body ctx f : D.t = - ctx.local + let body man f : D.t = + man.local - let special ctx lv f arglist : D.t = + let special man lv f arglist : D.t = let desc = LF.find f in match desc.special arglist with (* TODO: remove Lock/Unlock cases when all those libraryfunctions use librarydescs and don't read mutex contents *) | Lock _ | Unlock _ -> - ctx.local + man.local | _ -> LibraryDesc.Accesses.iter desc.accs (fun {kind; deep = reach} exp -> - access_one_top ~deref:true ctx kind reach exp (* access dereferenced using special accesses *) + access_one_top ~deref:true man kind reach exp (* access dereferenced using special accesses *) ) arglist; (match lv with - | Some x -> access_one_top ~deref:true ctx Write false (AddrOf x) + | Some x -> access_one_top ~deref:true man Write false (AddrOf x) | None -> ()); - List.iter (access_one_top ctx Read false) arglist; (* always read all argument expressions without dereferencing *) - ctx.local + List.iter (access_one_top man Read false) arglist; (* always read all argument expressions without dereferencing *) + man.local - let enter ctx lv f args : (D.t * D.t) list = - [(ctx.local,ctx.local)] + let enter man lv f args : (D.t * D.t) list = + [(man.local,man.local)] - let combine_env ctx lval fexp f args fc au f_ask = + let combine_env man lval fexp f args fc au f_ask = (* These should be in enter, but enter cannot emit events, nor has fexp argument *) - access_one_top ctx Read false fexp; - List.iter (access_one_top ctx Read false) args; + access_one_top man Read false fexp; + List.iter (access_one_top man Read false) args; au - let combine_assign ctx lv fexp f args fc al f_ask = + let combine_assign man lv fexp f args fc al f_ask = begin match lv with | None -> () - | Some lval -> access_one_top ~deref:true ctx Write false (AddrOf lval) + | Some lval -> access_one_top ~deref:true man Write false (AddrOf lval) end; - ctx.local + man.local - let threadspawn ctx ~multiple lval f args fctx = + let threadspawn man ~multiple lval f args fman = (* must explicitly access thread ID lval because special to pthread_create doesn't if singlethreaded before *) begin match lval with | None -> () - | Some lval -> access_one_top ~force:true ~deref:true ctx Write false (AddrOf lval) (* must force because otherwise doesn't if singlethreaded before *) + | Some lval -> access_one_top ~force:true ~deref:true man Write false (AddrOf lval) (* must force because otherwise doesn't if singlethreaded before *) end; - ctx.local + man.local - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | MayAccessed -> - (ctx.global ctx.node: G.t) + (man.global man.node: G.t) | _ -> Queries.Result.top q - let event ctx e octx = + let event man e oman = match e with | Events.Access {ad; kind; _} when !collect_local && !AnalysisState.postsolving -> let events = Queries.AD.fold (fun addr es -> @@ -151,9 +151,9 @@ struct | _ -> es ) ad (G.empty ()) in - ctx.sideg ctx.node events + man.sideg man.node events | _ -> - ctx.local + man.local end let _ = diff --git a/src/analyses/activeLongjmp.ml b/src/analyses/activeLongjmp.ml index 47e2432662..df5fdf5ff5 100644 --- a/src/analyses/activeLongjmp.ml +++ b/src/analyses/activeLongjmp.ml @@ -12,25 +12,25 @@ struct (* The first component are the longjmp targets, the second are the longjmp callers *) module D = JmpBufDomain.ActiveLongjmps - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in match desc.special arglist, f.vname with | Longjmp {env; value}, _ -> (* Set target to current value of env *) - let bufs = ctx.ask (EvalJumpBuf env) in - bufs, JmpBufDomain.NodeSet.singleton(ctx.prev_node) - | _ -> ctx.local + let bufs = man.ask (EvalJumpBuf env) in + bufs, JmpBufDomain.NodeSet.singleton(man.prev_node) + | _ -> man.local (* Initial values don't really matter: overwritten at longjmp call. *) let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] + let threadenter man ~multiple lval f args = [D.bot ()] let exitstate v = D.top () - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | ActiveJumpBuf -> (* Does not compile without annotation: "This instance (...) is ambiguous: it would escape the scope of its equation" *) - (ctx.local:JmpBufDomain.ActiveLongjmps.t) + (man.local:JmpBufDomain.ActiveLongjmps.t) | _ -> Queries.Result.top q end diff --git a/src/analyses/activeSetjmp.ml b/src/analyses/activeSetjmp.ml index 69db900d4c..d10ac11247 100644 --- a/src/analyses/activeSetjmp.ml +++ b/src/analyses/activeSetjmp.ml @@ -13,24 +13,24 @@ struct include Analyses.ValueContexts(D) module P = IdentityP (D) - let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask): D.t = - ctx.local (* keep local as opposed to IdentitySpec *) + let combine_env man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask): D.t = + man.local (* keep local as opposed to IdentitySpec *) - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in match desc.special arglist with | Setjmp _ -> - let entry = (ctx.prev_node, ctx.control_context ()) in - D.add (Target entry) ctx.local - | _ -> ctx.local + let entry = (man.prev_node, man.control_context ()) in + D.add (Target entry) man.local + | _ -> man.local let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] + let threadenter man ~multiple lval f args = [D.bot ()] let exitstate v = D.top () - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with - | ValidLongJmp -> (ctx.local: D.t) + | ValidLongJmp -> (man.local: D.t) | _ -> Queries.Result.top q end diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index df3cf545c5..9be61523de 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -40,7 +40,7 @@ struct (* Result map used for comparison of results for relational traces paper. *) let results = PCU.RH.create 103 - let context ctx fd x = + let context man fd x = if ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.relation.context" ~removeAttr:"relation.no-context" ~keepAttr:"relation.context" fd then x else @@ -189,8 +189,8 @@ struct else not (ask.f (MaySignedOverflow exp)) - let no_overflow ctx exp = lazy ( - let res = no_overflow ctx exp in + let no_overflow man exp = lazy ( + let res = no_overflow man exp in if M.tracing then M.tracel "no_ov" "no_ov %b exp: %a" res d_exp exp; res ) @@ -245,13 +245,13 @@ struct inner e (* Basic transfer functions. *) - let assign ctx (lv:lval) e = - let st = ctx.local in - let simplified_e = replace_deref_exps ctx.ask e in + let assign man (lv:lval) e = + let st = man.local in + let simplified_e = replace_deref_exps man.ask e in if M.tracing then M.traceli "relation" "assign %a = %a (simplified to %a)" d_lval lv d_exp e d_exp simplified_e; - let ask = Analyses.ask_of_ctx ctx in - let r = assign_to_global_wrapper ask ctx.global ctx.sideg st lv (fun st v -> - assign_from_globals_wrapper ask ctx.global st simplified_e (fun apr' e' -> + let ask = Analyses.ask_of_man man in + let r = assign_to_global_wrapper ask man.global man.sideg st lv (fun st v -> + assign_from_globals_wrapper ask man.global st simplified_e (fun apr' e' -> if M.tracing then M.traceli "relation" "assign inner %a = %a (%a)" CilType.Varinfo.pretty v d_exp e' d_plainexp e'; if M.tracing then M.trace "relation" "st: %a" RD.pretty apr'; let r = RD.assign_exp ask apr' (RV.local v) e' (no_overflow ask simplified_e) in @@ -263,10 +263,10 @@ struct if M.tracing then M.traceu "relation" "-> %a" D.pretty r; r - let branch ctx e b = - let st = ctx.local in - let ask = Analyses.ask_of_ctx ctx in - let res = assign_from_globals_wrapper ask ctx.global st e (fun rel' e' -> + let branch man e b = + let st = man.local in + let ask = Analyses.ask_of_man man in + let res = assign_from_globals_wrapper ask man.global st e (fun rel' e' -> (* not an assign, but must remove g#in-s still *) RD.assert_inv ask rel' e' (not b) (no_overflow ask e) ) @@ -282,9 +282,9 @@ struct let locals_id = List.map (fun v -> v.vid) locals in VS.exists (fun v -> List.mem v.vid locals_id && RD.Tracked.varinfo_tracked v) reachable_from_args - let reachable_from_args ctx args = + let reachable_from_args man args = let to_vs e = - ctx.ask (ReachableFrom e) + man.ask (ReachableFrom e) |> Queries.AD.to_var_may |> VS.of_list in @@ -301,9 +301,9 @@ struct | None -> true | Some v -> any_local_reachable - let make_callee_rel ~thread ctx f args = - let fundec = Node.find_fundec ctx.node in - let st = ctx.local in + let make_callee_rel ~thread man f args = + let fundec = Node.find_fundec man.node in + let st = man.local in let arg_assigns = GobList.combine_short f.sformals args (* TODO: is it right to ignore missing formals/args? *) |> List.filter_map (fun (x, e) -> if RD.Tracked.varinfo_tracked x then Some (RV.arg x, e) else None) @@ -316,14 +316,14 @@ struct if thread then new_rel else - let ask = Analyses.ask_of_ctx ctx in + let ask = Analyses.ask_of_man man in List.fold_left (fun new_rel (var, e) -> - assign_from_globals_wrapper ask ctx.global {st with rel = new_rel} e (fun rel' e' -> + assign_from_globals_wrapper ask man.global {st with rel = new_rel} e (fun rel' e' -> RD.assign_exp ask rel' var e' (no_overflow ask e) ) ) new_rel arg_assigns in - let reachable_from_args = reachable_from_args ctx args in + let reachable_from_args = reachable_from_args man args in let any_local_reachable = any_local_reachable fundec reachable_from_args in RD.remove_filter_with new_rel (fun var -> match RV.find_metadata var with @@ -334,13 +334,13 @@ struct if M.tracing then M.tracel "combine" "relation enter newd: %a" RD.pretty new_rel; new_rel - let enter ctx r f args = - let calle_rel = make_callee_rel ~thread:false ctx f args in - [ctx.local, {ctx.local with rel = calle_rel}] + let enter man r f args = + let calle_rel = make_callee_rel ~thread:false man f args in + [man.local, {man.local with rel = calle_rel}] - let body ctx f = - let st = ctx.local in - let ask = Analyses.ask_of_ctx ctx in + let body man f = + let st = man.local in + let ask = Analyses.ask_of_man man in let formals = List.filter RD.Tracked.varinfo_tracked f.sformals in let locals = List.filter RD.Tracked.varinfo_tracked f.slocals in let new_rel = RD.add_vars st.rel (List.map RV.local (formals @ locals)) in @@ -353,15 +353,15 @@ struct RD.assign_var_parallel_with new_rel local_assigns; (* doesn't need to be parallel since arg vars aren't local vars *) {st with rel = new_rel} - let return ctx e f = - let st = ctx.local in - let ask = Analyses.ask_of_ctx ctx in + let return man e f = + let st = man.local in + let ask = Analyses.ask_of_man man in let new_rel = if RD.Tracked.type_tracked (Cilfacade.fundec_return_type f) then ( let rel' = RD.add_vars st.rel [RV.return] in match e with | Some e -> - assign_from_globals_wrapper ask ctx.global {st with rel = rel'} e (fun rel' e' -> + assign_from_globals_wrapper ask man.global {st with rel = rel'} e (fun rel' e' -> RD.assign_exp ask rel' RV.return e' (no_overflow ask e) ) | None -> @@ -379,15 +379,15 @@ struct let st' = {st with rel = new_rel} in begin match ThreadId.get_current ask with | `Lifted tid when ThreadReturn.is_current ask -> - Priv.thread_return ask ctx.global ctx.sideg tid st' + Priv.thread_return ask man.global man.sideg tid st' | _ -> st' end - let combine_env ctx r fe f args fc fun_st (f_ask : Queries.ask) = - let st = ctx.local in - let reachable_from_args = reachable_from_args ctx args in - let fundec = Node.find_fundec ctx.node in + let combine_env man r fe f args fc fun_st (f_ask : Queries.ask) = + let st = man.local in + let reachable_from_args = reachable_from_args man args in + let fundec = Node.find_fundec man.node in if M.tracing then M.tracel "combine-rel" "relation f: %a" CilType.Varinfo.pretty f.svar; if M.tracing then M.tracel "combine-rel" "relation formals: %a" (d_list "," CilType.Varinfo.pretty) f.sformals; if M.tracing then M.tracel "combine-rel" "relation args: %a" (d_list "," d_exp) args; @@ -406,9 +406,9 @@ struct in (* RD.substitute_exp_parallel_with new_fun_rel arg_substitutes; (* doesn't need to be parallel since exps aren't arg vars directly *) *) (* TODO: parallel version of assign_from_globals_wrapper? *) - let ask = Analyses.ask_of_ctx ctx in + let ask = Analyses.ask_of_man man in let new_fun_rel = List.fold_left (fun new_fun_rel (var, e) -> - assign_from_globals_wrapper ask ctx.global {st with rel = new_fun_rel} e (fun rel' e' -> + assign_from_globals_wrapper ask man.global {st with rel = new_fun_rel} e (fun rel' e' -> (* not an assign, but still works? *) (* substitute is the backwards semantics of assignment *) (* https://antoinemine.github.io/Apron/doc/papers/expose_CEA_2007.pdf *) @@ -435,13 +435,13 @@ struct if M.tracing then M.tracel "combine-rel" "relation unifying %a %a = %a" RD.pretty new_rel RD.pretty new_fun_rel RD.pretty unify_rel; {fun_st with rel = unify_rel} - let combine_assign ctx r fe f args fc fun_st (f_ask : Queries.ask) = - let unify_st = ctx.local in + let combine_assign man r fe f args fc fun_st (f_ask : Queries.ask) = + let unify_st = man.local in if RD.Tracked.type_tracked (Cilfacade.fundec_return_type f) then ( let unify_st' = match r with | Some lv -> - let ask = Analyses.ask_of_ctx ctx in - assign_to_global_wrapper ask ctx.global ctx.sideg unify_st lv (fun st v -> + let ask = Analyses.ask_of_man man in + assign_to_global_wrapper ask man.global man.sideg unify_st lv (fun st v -> let rel = RD.assign_var st.rel (RV.local v) RV.return in assert_type_bounds ask rel v (* TODO: should be done in return instead *) ) @@ -455,8 +455,8 @@ struct unify_st - let invalidate_one ask ctx st lv = - assign_to_global_wrapper ask ctx.global ctx.sideg st lv (fun st v -> + let invalidate_one ask man st lv = + assign_to_global_wrapper ask man.global man.sideg st lv (fun st v -> let rel' = RD.forget_vars st.rel [RV.local v] in assert_type_bounds ask rel' v (* re-establish type bounds after forget *) (* TODO: no_overflow on wrapped *) ) @@ -477,8 +477,8 @@ struct List.fold_right reachable es (Some (Queries.AD.empty ())) - let forget_reachable ctx st es = - let ask = Analyses.ask_of_ctx ctx in + let forget_reachable man st es = + let ask = Analyses.ask_of_man man in let rs = match reachables ask es with | None -> @@ -495,17 +495,17 @@ struct Queries.AD.fold to_cil ad [] in List.fold_left (fun st lval -> - invalidate_one ask ctx st lval + invalidate_one ask man st lval ) st rs - let assert_fn ctx e refine = + let assert_fn man e refine = if not refine then - ctx.local + man.local else (* copied from branch *) - let st = ctx.local in - let ask = Analyses.ask_of_ctx ctx in - let res = assign_from_globals_wrapper ask ctx.global st e (fun apr' e' -> + let st = man.local in + let ask = Analyses.ask_of_man man in + let res = assign_from_globals_wrapper ask man.global st e (fun apr' e' -> (* not an assign, but must remove g#in-s still *) RD.assert_inv ask apr' e' false (no_overflow ask e) ) @@ -513,19 +513,19 @@ struct if RD.is_bot_env res then raise Deadcode; {st with rel = res} - let special ctx r f args = - let ask = Analyses.ask_of_ctx ctx in - let st = ctx.local in + let special man r f args = + let ask = Analyses.ask_of_man man in + let st = man.local in let desc = LibraryFunctions.find f in match desc.special args, f.vname with - | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine + | Assert { exp; refine; _ }, _ -> assert_fn man exp refine | ThreadJoin { thread = id; ret_var = retvar }, _ -> ( (* Forget value that thread return is assigned to *) - let st' = forget_reachable ctx st [retvar] in - let st' = Priv.thread_join ask ctx.global id st' in + let st' = forget_reachable man st [retvar] in + let st' = Priv.thread_join ask man.global id st' in match r with - | Some lv -> invalidate_one ask ctx st' lv + | Some lv -> invalidate_one ask man st' lv | None -> st' ) | ThreadExit _, _ -> @@ -533,19 +533,19 @@ struct | `Lifted tid -> (* value returned from the thread is not used in thread_join or any Priv.thread_join, *) (* thus no handling like for returning from functions required *) - ignore @@ Priv.thread_return ask ctx.global ctx.sideg tid st; + ignore @@ Priv.thread_return ask man.global man.sideg tid st; raise Deadcode | _ -> raise Deadcode end | Unknown, "__goblint_assume_join" -> let id = List.hd args in - Priv.thread_join ~force:true ask ctx.global id st + Priv.thread_join ~force:true ask man.global id st | Rand, _ -> (match r with | Some lv -> - let st = invalidate_one ask ctx st lv in - assert_fn {ctx with local = st} (BinOp (Ge, Lval lv, zero, intType)) true + let st = invalidate_one ask man st lv in + assert_fn {man with local = st} (BinOp (Ge, Lval lv, zero, intType)) true | None -> st) | _, _ -> let lvallist e = @@ -570,33 +570,33 @@ struct else deep_addrs in - let st' = forget_reachable ctx st deep_addrs in + let st' = forget_reachable man st deep_addrs in let shallow_lvals = List.concat_map lvallist shallow_addrs in - let st' = List.fold_left (invalidate_one ask ctx) st' shallow_lvals in + let st' = List.fold_left (invalidate_one ask man) st' shallow_lvals in (* invalidate lval if present *) match r with - | Some lv -> invalidate_one ask ctx st' lv + | Some lv -> invalidate_one ask man st' lv | None -> st' - let query_invariant ctx context = + let query_invariant man context = let keep_local = GobConfig.get_bool "ana.relation.invariant.local" in let keep_global = GobConfig.get_bool "ana.relation.invariant.global" in let one_var = GobConfig.get_bool "ana.relation.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 + let ask = Analyses.ask_of_man man in + let scope = Node.find_fundec man.node in let (apr, e_inv) = if ThreadFlag.has_ever_been_multi ask then ( let priv_vars = if keep_global then - Priv.invariant_vars ask ctx.global ctx.local + Priv.invariant_vars ask man.global man.local else [] (* avoid pointless queries *) in - let (rel, e_inv, v_ins_inv) = read_globals_to_locals_inv ask ctx.global ctx.local priv_vars in + let (rel, e_inv, v_ins_inv) = read_globals_to_locals_inv ask man.global man.local priv_vars in (* filter variables *) let var_filter v = match RV.find_metadata v with | Some (Local v) -> @@ -616,7 +616,7 @@ struct | Some (Local _) -> keep_local | _ -> false in - let apr = RD.keep_filter ctx.local.rel var_filter in + let apr = RD.keep_filter man.local.rel var_filter in (apr, Fun.id) ) in @@ -634,90 +634,90 @@ struct ) |> Enum.fold (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none - let query_invariant_global ctx g = - if GobConfig.get_bool "ana.relation.invariant.global" && ctx.ask (GhostVarAvailable Multithreaded) then ( + let query_invariant_global man g = + if GobConfig.get_bool "ana.relation.invariant.global" && man.ask (GhostVarAvailable Multithreaded) then ( let var = WitnessGhost.to_varinfo Multithreaded in - let inv = Priv.invariant_global (Analyses.ask_of_ctx ctx) ctx.global g in + let inv = Priv.invariant_global (Analyses.ask_of_man man) man.global g in Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) else Invariant.none - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = let open Queries in - let st = ctx.local in + let st = man.local in let eval_int e no_ov = - let esimple = replace_deref_exps ctx.ask e in + let esimple = replace_deref_exps man.ask e in read_from_globals_wrapper - (Analyses.ask_of_ctx ctx) - ctx.global st esimple - (fun rel' e' -> RD.eval_int (Analyses.ask_of_ctx ctx) rel' e' no_ov) + (Analyses.ask_of_man man) + man.global st esimple + (fun rel' e' -> RD.eval_int (Analyses.ask_of_man man) rel' e' no_ov) in match q with | EvalInt e -> if M.tracing then M.traceli "evalint" "relation query %a (%a)" d_exp e d_plainexp e; - if M.tracing then M.trace "evalint" "relation st: %a" D.pretty ctx.local; - let r = eval_int e (no_overflow (Analyses.ask_of_ctx ctx) e) in + if M.tracing then M.trace "evalint" "relation st: %a" D.pretty man.local; + let r = eval_int e (no_overflow (Analyses.ask_of_man man) e) in if M.tracing then M.traceu "evalint" "relation query %a -> %a" d_exp e ID.pretty r; r | Queries.IterSysVars (vq, vf) -> let vf' x = vf (Obj.repr x) in - Priv.iter_sys_vars ctx.global vq vf' - | Queries.Invariant context -> query_invariant ctx context + Priv.iter_sys_vars man.global vq vf' + | Queries.Invariant context -> query_invariant man context | Queries.InvariantGlobal g -> let g: V.t = Obj.obj g in - query_invariant_global ctx g + query_invariant_global man g | _ -> Result.top q (* Thread transfer functions. *) - let threadenter ctx ~multiple lval f args = - let st = ctx.local in + let threadenter man ~multiple lval f args = + let st = man.local in match Cilfacade.find_varinfo_fundec f with | fd -> (* TODO: HACK: Simulate enter_multithreaded for first entering thread to publish global inits before analyzing thread. Otherwise thread is analyzed with no global inits, reading globals gives bot, which turns into top, which might get published... sync `Thread doesn't help us here, it's not specific to entering multithreaded mode. EnterMultithreaded events only execute after threadenter and threadspawn. *) - if not (ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx)) then - ignore (Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st); - let st' = Priv.threadenter (Analyses.ask_of_ctx ctx) ctx.global st in - let new_rel = make_callee_rel ~thread:true ctx fd args in + if not (ThreadFlag.has_ever_been_multi (Analyses.ask_of_man man)) then + ignore (Priv.enter_multithreaded (Analyses.ask_of_man man) man.global man.sideg st); + let st' = Priv.threadenter (Analyses.ask_of_man man) man.global st in + let new_rel = make_callee_rel ~thread:true man fd args in [{st' with rel = new_rel}] | exception Not_found -> (* Unknown functions *) (* TODO: do something like base? *) failwith "relation.threadenter: unknown function" - let threadspawn ctx ~multiple lval f args fctx = - ctx.local + let threadspawn man ~multiple lval f args fman = + man.local - let event ctx e octx = - let ask = Analyses.ask_of_ctx ctx in - let st = ctx.local in + let event man e oman = + let ask = Analyses.ask_of_man man in + let st = man.local in match e with | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) CommonPriv.lift_lock ask (fun st m -> - Priv.lock ask ctx.global st m + Priv.lock ask man.global st m ) st addr | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) WideningTokenLifter.with_local_side_tokens (fun () -> CommonPriv.lift_unlock ask (fun st m -> - Priv.unlock ask ctx.global ctx.sideg st m + Priv.unlock ask man.global man.sideg st m ) st addr ) | Events.EnterMultiThreaded -> - Priv.enter_multithreaded (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st + Priv.enter_multithreaded (Analyses.ask_of_man man) man.global man.sideg st | Events.Escape escaped -> - Priv.escape ctx.node (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg st escaped + Priv.escape man.node (Analyses.ask_of_man man) man.global man.sideg st escaped | Assert exp -> - assert_fn ctx exp true + assert_fn man exp true | Events.Unassume {exp = e; tokens} -> let e_orig = e in - let ask = Analyses.ask_of_ctx ctx in - let e = replace_deref_exps ctx.ask e in - let (rel, e, v_ins) = read_globals_to_locals ask ctx.global ctx.local e in + let ask = Analyses.ask_of_man man in + let e = replace_deref_exps man.ask e in + let (rel, e, v_ins) = read_globals_to_locals ask man.global man.local e in let vars = Basetype.CilExp.get_vars e |> List.unique ~eq:CilType.Varinfo.equal |> List.filter RD.Tracked.varinfo_tracked in let rel = RD.forget_vars rel (List.map RV.local vars) in (* havoc *) @@ -732,12 +732,12 @@ struct let esimple = replace_deref_exps dummyask.f e in read_from_globals_wrapper (dummyask) - ctx.global { st with rel } esimple + man.global { st with rel } esimple (fun rel' e' -> RD.eval_int (dummyask) rel' e' no_ov) in match q with | EvalInt e -> - if M.tracing then M.traceli "relation" "evalint query %a (%a), ctx %a" d_exp e d_plainexp e D.pretty ctx.local; + if M.tracing then M.traceli "relation" "evalint query %a (%a), man %a" d_exp e d_plainexp e D.pretty man.local; let r = eval_int e (no_overflow (dummyask) e) in if M.tracing then M.trace "relation" "evalint response %a -> %a" d_exp e ValueDomainQueries.ID.pretty r; r @@ -752,21 +752,21 @@ struct WideningTokenLifter.with_side_tokens (WideningTokenLifter.TS.of_list tokens) (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 rel} + write_global ask man.global man.sideg st v v_in + ) v_ins {man.local with rel} ) in let rel = RD.remove_vars st.rel (List.map RV.local (VH.values v_ins |> List.of_enum)) in (* remove temporary g#in-s *) if M.tracing then M.traceli "apron" "unassume join"; - let st = D.join ctx.local {st with rel} in (* (strengthening) join *) + let st = D.join man.local {st with rel} in (* (strengthening) join *) if M.tracing then M.traceu "apron" "unassume join"; M.info ~category:Witness "relation unassumed invariant: %a" d_exp e_orig; st | _ -> st - let sync ctx reason = + let sync man reason = (* After the solver is finished, store the results (for later comparison) *) if !AnalysisState.postsolving && GobConfig.get_string "exp.relation.prec-dump" <> "" then begin let keep_local = GobConfig.get_bool "ana.relation.invariant.local" in @@ -778,13 +778,13 @@ struct | Some (Local _) -> keep_local | _ -> false in - let st = RD.keep_filter ctx.local.rel var_filter in - let old_value = PCU.RH.find_default results ctx.node (RD.bot ()) in + let st = RD.keep_filter man.local.rel var_filter in + let old_value = PCU.RH.find_default results man.node (RD.bot ()) in let new_value = RD.join old_value st in - PCU.RH.replace results ctx.node new_value; + PCU.RH.replace results man.node new_value; end; WideningTokenLifter.with_local_side_tokens (fun () -> - Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread]) + Priv.sync (Analyses.ask_of_man man) man.global man.sideg man.local (reason :> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread]) ) let init marshal = diff --git a/src/analyses/assert.ml b/src/analyses/assert.ml index 8247a0d7e8..c9045037f7 100644 --- a/src/analyses/assert.ml +++ b/src/analyses/assert.ml @@ -13,10 +13,10 @@ struct (* transfer functions *) - let assert_fn ctx e check refine = + let assert_fn man e check refine = let check_assert e st = - match ctx.ask (Queries.EvalInt e) with + match man.ask (Queries.EvalInt e) with | v when Queries.ID.is_bot v -> `Bot | v -> match Queries.ID.to_bool v with @@ -41,25 +41,25 @@ struct warn_fn msg in (* TODO: use format instead of %s for the following messages *) - match check_assert e ctx.local with + match check_assert e man.local with | `Lifted false -> warn (M.error ~category:Assert "%s") ~annot:"FAIL" ("Assertion \"" ^ expr ^ "\" will fail."); - if refine then raise Analyses.Deadcode else ctx.local + if refine then raise Analyses.Deadcode else man.local | `Lifted true -> warn (M.success ~category:Assert "%s") ("Assertion \"" ^ expr ^ "\" will succeed"); - ctx.local + man.local | `Bot -> M.error ~category:Assert "%s" ("Assertion \"" ^ expr ^ "\" produces a bottom. What does that mean? (currently uninitialized arrays' content is bottom)"); - ctx.local + man.local | `Top -> warn (M.warn ~category:Assert "%s") ~annot:"UNKNOWN" ("Assertion \"" ^ expr ^ "\" is unknown."); - ctx.local + man.local - let special ctx (lval: lval option) (f:varinfo) (args:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (args:exp list) : D.t = let desc = LibraryFunctions.find f in match desc.special args, f.vname with - | Assert { exp; check; refine }, _ -> assert_fn ctx exp check refine - | _, _ -> ctx.local + | Assert { exp; check; refine }, _ -> assert_fn man exp check refine + | _, _ -> man.local end let _ = diff --git a/src/analyses/base.ml b/src/analyses/base.ml index f6e41361a3..f24ae36b2c 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -143,8 +143,8 @@ struct * Initializing my variables **************************************************************************) - let heap_var on_stack ctx = - let info = match (ctx.ask (Q.AllocVar {on_stack})) with + let heap_var on_stack man = + let info = match (man.ask (Q.AllocVar {on_stack})) with | `Lifted vinfo -> vinfo | _ -> failwith("Ran without a malloc analysis.") in info @@ -243,7 +243,7 @@ struct | _ -> false (* Evaluate binop for two abstract values: *) - let evalbinop_base ~ctx (op: binop) (t1:typ) (a1:value) (t2:typ) (a2:value) (t:typ) :value = + let evalbinop_base ~man (op: binop) (t1:typ) (a1:value) (t2:typ) (a2:value) (t:typ) :value = if M.tracing then M.tracel "eval" "evalbinop %a %a %a" d_binop op VD.pretty a1 VD.pretty a2; (* We define a conversion function for the easy cases when we can just use * the integer domain operations. *) @@ -359,7 +359,7 @@ struct let ax = AD.choose x in let ay = AD.choose y in let handle_address_is_multiple addr = begin match Addr.to_var addr with - | Some v when ctx.ask (Q.IsMultiple v) -> + | Some v when man.ask (Q.IsMultiple v) -> if M.tracing then M.tracel "addr" "IsMultiple %a" CilType.Varinfo.pretty v; None | _ -> @@ -434,32 +434,32 @@ struct * State functions **************************************************************************) - let sync' reason ctx: D.t = + let sync' reason man: D.t = let multi = match reason with | `Init | `Thread -> true | _ -> - ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) + ThreadFlag.has_ever_been_multi (Analyses.ask_of_man man) in if M.tracing then M.tracel "sync" "sync multi=%B earlyglobs=%B" multi !earlyglobs; if !earlyglobs || multi then WideningTokenLifter.with_local_side_tokens (fun () -> - Priv.sync (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) ctx.local reason + Priv.sync (Analyses.ask_of_man man) (priv_getg man.global) (priv_sideg man.sideg) man.local reason ) else - ctx.local + man.local - let sync ctx reason = sync' (reason :> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread]) ctx + let sync man reason = sync' (reason :> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread]) man - let publish_all ctx reason = - ignore (sync' reason ctx) + let publish_all man reason = + ignore (sync' reason man) - let get_var ~ctx (st: store) (x: varinfo): value = - let ask = Analyses.ask_of_ctx ctx in + let get_var ~man (st: store) (x: varinfo): value = + let ask = Analyses.ask_of_man man in if (!earlyglobs || ThreadFlag.has_ever_been_multi ask) && is_global ask x then - Priv.read_global ask (priv_getg ctx.global) st x + Priv.read_global ask (priv_getg man.global) st x else begin if M.tracing then M.tracec "get" "Singlethreaded mode."; CPA.find x st.cpa @@ -469,15 +469,15 @@ struct * adding proper dependencies. * For the exp argument it is always ok to put None. This means not using precise information about * which part of an array is involved. *) - let rec get ~ctx ?(top=VD.top ()) ?(full=false) (st: store) (addrs:address) (exp:exp option): value = + let rec get ~man ?(top=VD.top ()) ?(full=false) (st: store) (addrs:address) (exp:exp option): value = let firstvar = if M.tracing then match AD.to_var_may addrs with [] -> "" | x :: _ -> x.vname else "" in if M.tracing then M.traceli "get" ~var:firstvar "Address: %a\nState: %a" AD.pretty addrs CPA.pretty st.cpa; (* Finding a single varinfo*offset pair *) let res = let f_addr (x, offs) = (* get hold of the variable value, either from local or global state *) - let var = get_var ~ctx st x in - let v = VD.eval_offset (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) (fun x -> get ~ctx st x exp) var offs exp (Some (Var x, Offs.to_cil_offset offs)) x.vtype in + let var = get_var ~man st x in + let v = VD.eval_offset (Queries.to_value_domain_ask (Analyses.ask_of_man man)) (fun x -> get ~man st x exp) var offs exp (Some (Var x, Offs.to_cil_offset offs)) x.vtype in if M.tracing then M.tracec "get" "var = %a, %a = %a" VD.pretty var AD.pretty (AD.of_mval (x, offs)) VD.pretty v; if full then var else match v with | Blob (c,s,_) -> c @@ -558,9 +558,9 @@ struct (* Get the list of addresses accessable immediately from a given address, thus * all pointers within a structure should be considered, but we don't follow * pointers. We return a flattend representation, thus simply an address (set). *) - let reachable_from_address ~ctx st (adr: address): address = + let reachable_from_address ~man st (adr: address): address = if M.tracing then M.tracei "reachability" "Checking for %a" AD.pretty adr; - let res = reachable_from_value (Analyses.ask_of_ctx ctx) (get ~ctx st adr None) (AD.type_of adr) (AD.show adr) in + let res = reachable_from_value (Analyses.ask_of_man man) (get ~man st adr None) (AD.type_of adr) (AD.show adr) in if M.tracing then M.traceu "reachability" "Reachable addresses: %a" AD.pretty res; res @@ -568,7 +568,7 @@ struct * This section is very confusing, because I use the same construct, a set of * addresses, as both AD elements abstracting individual (ambiguous) addresses * and the workset of visited addresses. *) - let reachable_vars ~ctx (st: store) (args: address list): address list = + let reachable_vars ~man (st: store) (args: address list): address list = if M.tracing then M.traceli "reachability" "Checking reachable arguments from [%a]!" (d_list ", " AD.pretty) args; let empty = AD.empty () in (* We begin looking at the parameters: *) @@ -581,7 +581,7 @@ struct (* ok, let's visit all the variables in the workset and collect the new variables *) let visit_and_collect var (acc: address): address = let var = AD.singleton var in (* Very bad hack! Pathetic really! *) - AD.union (reachable_from_address ~ctx st var) acc in + AD.union (reachable_from_address ~man st var) acc in let collected = AD.fold visit_and_collect !workset empty in (* And here we remove the already visited variables *) workset := AD.diff collected !visited @@ -590,7 +590,7 @@ struct if M.tracing then M.traceu "reachability" "All reachable vars: %a" AD.pretty !visited; List.map AD.singleton (AD.elements !visited) - let reachable_vars ~ctx st args = Timing.wrap "reachability" (reachable_vars ~ctx st) args + let reachable_vars ~man st args = Timing.wrap "reachability" (reachable_vars ~man st) args let drop_non_ptrs (st:CPA.t) : CPA.t = if CPA.is_top st then st else @@ -624,7 +624,7 @@ struct let drop_intervalSet = CPA.map (function Int x -> Int (ID.no_intervalSet x) | x -> x ) - let context ctx (fd: fundec) (st: store): store = + let context man (fd: fundec) (st: store): store = let f keep drop_fn (st: store) = if keep then st else { st with cpa = drop_fn st.cpa} in st |> (* Here earlyglobs only drops syntactic globals from the context and does not consider e.g. escaped globals. *) @@ -636,7 +636,7 @@ struct %> f (ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.base.context.interval_set" ~removeAttr:"base.no-interval_set" ~keepAttr:"base.interval_set" fd) drop_intervalSet - let reachable_top_pointers_types ctx (ps: AD.t) : Queries.TS.t = + let reachable_top_pointers_types man (ps: AD.t) : Queries.TS.t = let module TS = Queries.TS in let empty = AD.empty () in let reachable_from_address (adr: address) = @@ -662,7 +662,7 @@ struct | Address adrs when AD.is_top adrs -> (empty,TS.bot (), true) | Address adrs -> (adrs,TS.bot (), AD.may_be_unknown adrs) | Union (t,e) -> with_field (reachable_from_value e) t - | Array a -> reachable_from_value (ValueDomain.CArrays.get (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) a (None, ValueDomain.ArrIdxDomain.top ())) + | Array a -> reachable_from_value (ValueDomain.CArrays.get (Queries.to_value_domain_ask (Analyses.ask_of_man man)) a (None, ValueDomain.ArrIdxDomain.top ())) | Blob (e,_,_) -> reachable_from_value e | Struct s -> let join_tr (a1,t1,_) (a2,t2,_) = AD.join a1 a2, TS.join t1 t2, false in @@ -677,7 +677,7 @@ struct | JmpBuf _ -> (empty, TS.bot (), false) (* TODO: is this right? *) | Mutex -> (empty, TS.bot (), false) (* TODO: is this right? *) in - reachable_from_value (get ~ctx ctx.local adr None) + reachable_from_value (get ~man man.local adr None) in let visited = ref empty in let work = ref ps in @@ -697,14 +697,14 @@ struct !collected (* The evaluation function as mutually recursive eval_lv & eval_rv *) - let rec eval_rv ~(ctx: _ ctx) (st: store) (exp:exp): value = + let rec eval_rv ~(man: _ man) (st: store) (exp:exp): value = if M.tracing then M.traceli "evalint" "base eval_rv %a" d_exp exp; let r = (* we have a special expression that should evaluate to top ... *) if exp = MyCFG.unknown_exp then VD.top () else - eval_rv_ask_evalint ~ctx st exp + eval_rv_ask_evalint ~man st exp in if M.tracing then M.traceu "evalint" "base eval_rv %a -> %a" d_exp exp VD.pretty r; r @@ -713,14 +713,14 @@ struct Base itself also answers EvalInt, so recursion goes indirectly through queries. This allows every subexpression to also meet more precise value from other analyses. Non-integer expression just delegate to next eval_rv function. *) - and eval_rv_ask_evalint ~ctx st exp = - let eval_next () = eval_rv_no_ask_evalint ~ctx st exp in + and eval_rv_ask_evalint ~man st exp = + let eval_next () = eval_rv_no_ask_evalint ~man st exp in if M.tracing then M.traceli "evalint" "base eval_rv_ask_evalint %a" d_exp exp; let r:value = match Cilfacade.typeOf exp with | typ when Cil.isIntegralType typ && not (Cil.isConstant exp) -> (* don't EvalInt integer constants, base can do them precisely itself *) if M.tracing then M.traceli "evalint" "base ask EvalInt %a" d_exp exp; - let a = ctx.ask (Q.EvalInt exp) in (* through queries includes eval_next, so no (exponential) branching is necessary *) + let a = man.ask (Q.EvalInt exp) in (* through queries includes eval_next, so no (exponential) branching is necessary *) if M.tracing then M.traceu "evalint" "base ask EvalInt %a -> %a" d_exp exp Queries.ID.pretty a; begin match a with | `Bot -> eval_next () (* Base EvalInt returns bot on incorrect type (e.g. pthread_t); ignore and continue. *) @@ -737,24 +737,24 @@ struct (** Evaluate expression without EvalInt query on outermost expression. This is used by base responding to EvalInt to immediately directly avoid EvalInt query cycle, which would return top. Recursive [eval_rv] calls on subexpressions still go through [eval_rv_ask_evalint]. *) - and eval_rv_no_ask_evalint ~ctx st exp = - eval_rv_base ~ctx st exp (* just as alias, so query doesn't weirdly have to call eval_rv_base *) + and eval_rv_no_ask_evalint ~man st exp = + eval_rv_base ~man st exp (* just as alias, so query doesn't weirdly have to call eval_rv_base *) - and eval_rv_back_up ~ctx st exp = + and eval_rv_back_up ~man st exp = if get_bool "ana.base.eval.deep-query" then - eval_rv ~ctx st exp + eval_rv ~man st exp else ( (* duplicate unknown_exp check from eval_rv since we're bypassing it now *) if exp = MyCFG.unknown_exp then VD.top () else - eval_rv_base ~ctx st exp (* bypass all queries *) + eval_rv_base ~man st exp (* bypass all queries *) ) (** Evaluate expression structurally by base. This handles constants directly and variables using CPA. Subexpressions delegate to [eval_rv], which may use queries on them. *) - and eval_rv_base ~ctx (st: store) (exp:exp): value = + and eval_rv_base ~man (st: store) (exp:exp): value = let eval_rv = eval_rv_back_up in if M.tracing then M.traceli "evalint" "base eval_rv_base %a" d_exp exp; let binop_remove_same_casts ~extra_is_safe ~e1 ~e2 ~t1 ~t2 ~c1 ~c2 = @@ -776,7 +776,7 @@ struct match constFold true exp with (* Integer literals *) (* seems like constFold already converts CChr to CInt *) - | Const (CChr x) -> eval_rv ~ctx st (Const (charConstToInt x)) (* char becomes int, see Cil doc/ISO C 6.4.4.4.10 *) + | Const (CChr x) -> eval_rv ~man st (Const (charConstToInt x)) (* char becomes int, see Cil doc/ISO C 6.4.4.4.10 *) | Const (CInt (num,ikind,str)) -> (match str with Some x -> if M.tracing then M.tracel "casto" "CInt (%s, %a, %s)" (Z.to_string num) d_ikind ikind x | None -> ()); Int (ID.cast_to ikind (IntDomain.of_const (num,ikind,str))) @@ -792,21 +792,21 @@ struct | Const _ -> VD.top () (* Variables and address expressions *) | Lval lv -> - eval_rv_base_lval ~eval_lv ~ctx st exp lv + eval_rv_base_lval ~eval_lv ~man 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 -> - let a1 = eval_rv ~ctx st e1 in - let a2 = eval_rv ~ctx st e2 in + let a1 = eval_rv ~man st e1 in + let a2 = eval_rv ~man st e2 in let extra_is_safe = - match evalbinop_base ~ctx op t1 a1 t2 a2 typ with + match evalbinop_base ~man op t1 a1 t2 a2 typ with | Int i -> ID.to_bool i = Some true | _ | exception IntDomain.IncompatibleIKinds _ -> false in let (e1, e2) = binop_remove_same_casts ~extra_is_safe ~e1 ~e2 ~t1 ~t2 ~c1 ~c2 in (* re-evaluate e1 and e2 in evalbinop because might be with cast *) - evalbinop ~ctx st op ~e1 ~t1 ~e2 ~t2 typ + evalbinop ~man st op ~e1 ~t1 ~e2 ~t2 typ | BinOp (LOr, e1, e2, typ) as exp -> let open GobOption.Syntax in (* split nested LOr Eqs to equality pairs, if possible *) @@ -839,8 +839,8 @@ struct let eqs_value: value option = let* eqs = split exp in let* (e, es) = find_common eqs in - let v = eval_rv ~ctx st e in (* value of common exp *) - let vs = List.map (eval_rv ~ctx st) es in (* values of other sides *) + let v = eval_rv ~man st e in (* value of common exp *) + let vs = List.map (eval_rv ~man st) es in (* values of other sides *) let ik = Cilfacade.get_ikind typ in match v with | Address a -> @@ -882,25 +882,25 @@ struct in begin match eqs_value with | Some x -> x - | None -> evalbinop ~ctx st LOr ~e1 ~e2 typ (* fallback to general case *) + | None -> evalbinop ~man st LOr ~e1 ~e2 typ (* fallback to general case *) end | BinOp (op,e1,e2,typ) -> - evalbinop ~ctx st op ~e1 ~e2 typ + evalbinop ~man st op ~e1 ~e2 typ (* Unary operators *) | UnOp (op,arg1,typ) -> - let a1 = eval_rv ~ctx st arg1 in + let a1 = eval_rv ~man st arg1 in evalunop op typ a1 (* The &-operator: we create the address abstract element *) - | AddrOf lval -> Address (eval_lv ~ctx st lval) + | AddrOf lval -> Address (eval_lv ~man st lval) (* CIL's very nice implicit conversion of an array name [a] to a pointer * to its first element [&a[0]]. *) | StartOf lval -> let array_ofs = `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero, `NoOffset) in let array_start = add_offset_varinfo array_ofs in - Address (AD.map array_start (eval_lv ~ctx st lval)) - | CastE (t, Const (CStr (x,e))) -> (* VD.top () *) eval_rv ~ctx st (Const (CStr (x,e))) (* TODO safe? *) + Address (AD.map array_start (eval_lv ~man st lval)) + | CastE (t, Const (CStr (x,e))) -> (* VD.top () *) eval_rv ~man st (Const (CStr (x,e))) (* TODO safe? *) | CastE (t, exp) -> - (let v = eval_rv ~ctx st exp in + (let v = eval_rv ~man st exp in try VD.cast ~torg:(Cilfacade.typeOf exp) t v with Cilfacade.TypeOfError _ -> @@ -919,10 +919,10 @@ struct if M.tracing then M.traceu "evalint" "base eval_rv_base %a -> %a" d_exp exp VD.pretty r; r - and eval_rv_base_lval ~eval_lv ~ctx (st: store) (exp: exp) (lv: lval): value = + and eval_rv_base_lval ~eval_lv ~man (st: store) (exp: exp) (lv: lval): value = match lv with - | (Var v, ofs) -> get ~ctx st (eval_lv ~ctx st (Var v, ofs)) (Some exp) - (* | Lval (Mem e, ofs) -> get ~ctx st (eval_lv ~ctx (Mem e, ofs)) *) + | (Var v, ofs) -> get ~man st (eval_lv ~man st (Var v, ofs)) (Some exp) + (* | Lval (Mem e, ofs) -> get ~man st (eval_lv ~man (Mem e, ofs)) *) | (Mem e, ofs) -> (*if M.tracing then M.tracel "cast" "Deref: lval: %a" d_plainlval lv;*) let rec contains_vla (t:typ) = match t with @@ -934,7 +934,7 @@ struct in let b = Mem e, NoOffset in (* base pointer *) let t = Cilfacade.typeOfLval b in (* static type of base *) - let p = eval_lv ~ctx st b in (* abstract base addresses *) + let p = eval_lv ~man st b in (* abstract base addresses *) (* pre VLA: *) (* let cast_ok = function Addr a -> sizeOf t <= sizeOf (get_type_addr a) | _ -> false in *) let cast_ok a = @@ -966,35 +966,35 @@ struct let lookup_with_offs addr = let v = (* abstract base value *) if cast_ok addr then - get ~ctx ~top:(VD.top_value t) st (AD.singleton addr) (Some exp) (* downcasts are safe *) + get ~man ~top:(VD.top_value t) st (AD.singleton addr) (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!" VD.pretty v d_type t VD.pretty v'; - let v' = VD.eval_offset (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) (fun x -> get ~ctx st x (Some exp)) v' (convert_offset ~ctx st ofs) (Some exp) None t in (* handle offset *) + let v' = VD.eval_offset (Queries.to_value_domain_ask (Analyses.ask_of_man man)) (fun x -> get ~man st x (Some exp)) v' (convert_offset ~man st ofs) (Some exp) None t in (* handle offset *) v' in AD.fold (fun a acc -> VD.join acc (lookup_with_offs a)) p (VD.bot ()) - and evalbinop ~ctx (st: store) (op: binop) ~(e1:exp) ?(t1:typ option) ~(e2:exp) ?(t2:typ option) (t:typ): value = - evalbinop_mustbeequal ~ctx st op ~e1 ?t1 ~e2 ?t2 t + and evalbinop ~man (st: store) (op: binop) ~(e1:exp) ?(t1:typ option) ~(e2:exp) ?(t2:typ option) (t:typ): value = + evalbinop_mustbeequal ~man st op ~e1 ?t1 ~e2 ?t2 t (** Evaluate BinOp using MustBeEqual query as fallback. *) - and evalbinop_mustbeequal ~ctx (st: store) (op: binop) ~(e1:exp) ?(t1:typ option) ~(e2:exp) ?(t2:typ option) (t:typ): value = + and evalbinop_mustbeequal ~man (st: store) (op: binop) ~(e1:exp) ?(t1:typ option) ~(e2:exp) ?(t2:typ option) (t:typ): value = (* Evaluate structurally using base at first. *) - let a1 = eval_rv ~ctx st e1 in - let a2 = eval_rv ~ctx st e2 in + let a1 = eval_rv ~man st e1 in + let a2 = eval_rv ~man st e2 in let t1 = Option.default_delayed (fun () -> Cilfacade.typeOf e1) t1 in let t2 = Option.default_delayed (fun () -> Cilfacade.typeOf e2) t2 in - let r = evalbinop_base ~ctx op t1 a1 t2 a2 t in + let r = evalbinop_base ~man op t1 a1 t2 a2 t in if Cil.isIntegralType t then ( match r with | Int i when ID.to_int i <> None -> r (* Avoid fallback, cannot become any more precise. *) | _ -> (* Fallback to MustBeEqual query, could get extra precision from exprelation/var_eq. *) let must_be_equal () = - let r = Q.must_be_equal (Analyses.ask_of_ctx ctx) e1 e2 in + let r = Q.must_be_equal (Analyses.ask_of_man man) e1 e2 in if M.tracing then M.tracel "query" "MustBeEqual (%a, %a) = %b" d_exp e1 d_exp e2 r; r in @@ -1023,48 +1023,48 @@ struct (* A hackish evaluation of expressions that should immediately yield an * address, e.g. when calling functions. *) - and eval_fv ~ctx st (exp:exp): AD.t = + and eval_fv ~man st (exp:exp): AD.t = match exp with - | Lval lval -> eval_lv ~ctx st lval - | _ -> eval_tv ~ctx st exp + | Lval lval -> eval_lv ~man st lval + | _ -> eval_tv ~man st exp (* Used also for thread creation: *) - and eval_tv ~ctx st (exp:exp): AD.t = - match eval_rv ~ctx st exp with + and eval_tv ~man st (exp:exp): AD.t = + match eval_rv ~man st exp with | Address x -> x | _ -> failwith "Problems evaluating expression to function calls!" - and eval_int ~ctx st exp = - match eval_rv ~ctx st exp with + and eval_int ~man st exp = + match eval_rv ~man st exp with | Int x -> x | _ -> ID.top_of (Cilfacade.get_ikind_exp exp) (* A function to convert the offset to our abstract representation of * offsets, i.e. evaluate the index expression to the integer domain. *) - and convert_offset ~ctx (st: store) (ofs: offset) = + and convert_offset ~man (st: store) (ofs: offset) = let eval_rv = eval_rv_back_up in match ofs with | NoOffset -> `NoOffset - | Field (fld, ofs) -> `Field (fld, convert_offset ~ctx st ofs) + | Field (fld, ofs) -> `Field (fld, convert_offset ~man st ofs) | Index (exp, ofs) when CilType.Exp.equal exp (Lazy.force Offset.Index.Exp.any) -> (* special offset added by convertToQueryLval *) - `Index (IdxDom.top (), convert_offset ~ctx st ofs) + `Index (IdxDom.top (), convert_offset ~man st ofs) | Index (exp, ofs) -> - match eval_rv ~ctx st exp with - | Int i -> `Index (iDtoIdx i, convert_offset ~ctx st ofs) - | Address add -> `Index (AD.to_int add, convert_offset ~ctx st ofs) - | Top -> `Index (IdxDom.top (), convert_offset ~ctx st ofs) - | Bot -> `Index (IdxDom.bot (), convert_offset ~ctx st ofs) + match eval_rv ~man st exp with + | Int i -> `Index (iDtoIdx i, convert_offset ~man st ofs) + | Address add -> `Index (AD.to_int add, convert_offset ~man st ofs) + | Top -> `Index (IdxDom.top (), convert_offset ~man st ofs) + | Bot -> `Index (IdxDom.bot (), convert_offset ~man st ofs) | _ -> failwith "Index not an integer value" (* Evaluation of lvalues to our abstract address domain. *) - and eval_lv ~ctx st (lval:lval): AD.t = + and eval_lv ~man st (lval:lval): AD.t = let eval_rv = eval_rv_back_up in match lval with (* The simpler case with an explicit variable, e.g. for [x.field] we just * create the address { (x,field) } *) | Var x, ofs -> - AD.singleton (Addr.of_mval (x, convert_offset ~ctx st ofs)) + AD.singleton (Addr.of_mval (x, convert_offset ~man st ofs)) (* The more complicated case when [exp = & x.field] and we are asked to * evaluate [(\*exp).subfield]. We first evaluate [exp] to { (x,field) } * and then add the subfield to it: { (x,field.subfield) }. *) | Mem n, ofs -> begin - match eval_rv ~ctx st n with + match eval_rv ~man st n with | Address adr -> ( if AD.is_null adr then ( @@ -1077,14 +1077,14 @@ struct ); (* Warn if any of the addresses contains a non-local and non-global variable *) if AD.exists (function - | AD.Addr.Addr (v, _) -> not (CPA.mem v st.cpa) && not (is_global (Analyses.ask_of_ctx ctx) v) + | AD.Addr.Addr (v, _) -> not (CPA.mem v st.cpa) && not (is_global (Analyses.ask_of_man man) v) | _ -> false ) adr then ( AnalysisStateUtil.set_mem_safety_flag InvalidDeref; M.warn "lval %a points to a non-local variable. Invalid pointer dereference may occur" d_lval lval ) ); - AD.map (add_offset_varinfo (convert_offset ~ctx st ofs)) adr + AD.map (add_offset_varinfo (convert_offset ~man st ofs)) adr | _ -> M.debug ~category:Analyzer "Failed evaluating %a to lvalue" d_lval lval; AD.unknown_ptr @@ -1096,17 +1096,17 @@ struct (* run eval_rv from above, but change bot to top to be sound for programs with undefined behavior. *) (* Previously we only gave sound results for programs without undefined behavior, so yielding bot for accessing an uninitialized array was considered ok. Now only [invariant] can yield bot/Deadcode if the condition is known to be false but evaluating an expression should not be bot. *) - let eval_rv ~ctx (st: store) (exp:exp): value = + let eval_rv ~man (st: store) (exp:exp): value = try - let r = eval_rv ~ctx st exp in + let r = eval_rv ~man st exp in if M.tracing then M.tracel "eval" "eval_rv %a = %a" d_exp exp VD.pretty r; if VD.is_bot r then VD.top_value (Cilfacade.typeOf exp) else r with IntDomain.ArithmeticOnIntegerBot _ -> ValueDomain.Compound.top_value (Cilfacade.typeOf exp) - let query_evalint ~ctx st e = + let query_evalint ~man st e = if M.tracing then M.traceli "evalint" "base query_evalint %a" d_exp e; - let r = match eval_rv_no_ask_evalint ~ctx st e with + let r = match eval_rv_no_ask_evalint ~man st e with | Int i -> `Lifted i (* cast should be unnecessary, eval_rv should guarantee right ikind already *) | Bot -> Queries.ID.top () (* out-of-scope variables cause bot, but query result should then be unknown *) | Top -> Queries.ID.top () (* some float computations cause top (57-float/01-base), but query result should then be unknown *) @@ -1116,28 +1116,28 @@ struct if M.tracing then M.traceu "evalint" "base query_evalint %a -> %a" d_exp e Queries.ID.pretty r; r - (* Evaluate an expression containing only locals. This is needed for smart joining the partitioned arrays where ctx is not accessible. *) + (* Evaluate an expression containing only locals. This is needed for smart joining the partitioned arrays where man is not accessible. *) (* This will yield `Top for expressions containing any access to globals, and does not make use of the query system. *) (* Wherever possible, don't use this but the query system or normal eval_rv instead. *) let eval_exp st (exp:exp) = - (* Since ctx is not available here, we need to make some adjustments *) + (* Since man is not available here, we need to make some adjustments *) let rec 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 ( match q with - | EvalInt e -> query_evalint ~ctx:(ctx' (Queries.Set.add anyq asked)) st e (* mimic EvalInt query since eval_rv needs it *) + | EvalInt e -> query_evalint ~man:(man' (Queries.Set.add anyq asked)) st e (* mimic EvalInt query since eval_rv needs it *) | _ -> Queries.Result.top q ) and gs = function `Left _ -> `Lifted1 (Priv.G.top ()) | `Right _ -> `Lifted2 (VD.top ()) (* the expression is guaranteed to not contain globals *) - and ctx' asked = + and man' asked = { ask = (fun (type a) (q: a Queries.t) -> query asked q) ; emit = (fun _ -> failwith "Cannot \"emit\" in base eval_exp context.") ; node = MyCFG.dummy_node ; prev_node = MyCFG.dummy_node - ; control_context = (fun () -> ctx_failwith "Base eval_exp has no context.") - ; context = (fun () -> ctx_failwith "Base eval_exp has no context.") + ; control_context = (fun () -> man_failwith "Base eval_exp has no context.") + ; context = (fun () -> man_failwith "Base eval_exp has no context.") ; edge = MyCFG.Skip ; local = st ; global = gs @@ -1146,12 +1146,12 @@ struct ; sideg = (fun g d -> failwith "Base eval_exp trying to side effect.") } in - match eval_rv ~ctx:(ctx' Queries.Set.empty) st exp with + match eval_rv ~man:(man' Queries.Set.empty) st exp with | Int x -> ValueDomain.ID.to_int x | _ -> None - let eval_funvar ctx fval: Queries.AD.t = - let fp = eval_fv ~ctx ctx.local fval in + let eval_funvar man fval: Queries.AD.t = + let fp = eval_fv ~man man.local fval in if AD.is_top fp then ( if AD.cardinal fp = 1 then M.warn ~category:Imprecise ~tags:[Category Call] "Unknown call to function %a." d_exp fval @@ -1162,36 +1162,36 @@ struct (** Evaluate expression as address. Avoids expensive Apron EvalInt if the Int result would be useless to us anyway. *) - let eval_rv_address ~ctx st e = + let eval_rv_address ~man st e = (* no way to do eval_rv with expected type, so filter expression beforehand *) match Cilfacade.typeOf e with | t when Cil.isArithmeticType t -> (* definitely not address *) VD.top_value t | exception Cilfacade.TypeOfError _ (* something weird, might be address *) | _ -> - eval_rv ~ctx st e + eval_rv ~man st e (* interpreter end *) - let is_not_alloc_var ctx v = - not (ctx.ask (Queries.IsAllocVar v)) + let is_not_alloc_var man v = + not (man.ask (Queries.IsAllocVar v)) - let is_not_heap_alloc_var ctx v = - let is_alloc = ctx.ask (Queries.IsAllocVar v) in - not is_alloc || (is_alloc && not (ctx.ask (Queries.IsHeapVar v))) + let is_not_heap_alloc_var man v = + let is_alloc = man.ask (Queries.IsAllocVar v) in + not is_alloc || (is_alloc && not (man.ask (Queries.IsHeapVar v))) - let query_invariant ctx context = + let query_invariant man context = let keep_local = GobConfig.get_bool "ana.base.invariant.local" in let keep_global = GobConfig.get_bool "ana.base.invariant.global" in - let cpa = ctx.local.BaseDomain.cpa in - let ask = Analyses.ask_of_ctx ctx in + let cpa = man.local.BaseDomain.cpa in + let ask = Analyses.ask_of_man man in let module Arg = struct let context = context - let scope = Node.find_fundec ctx.node - let find v = get_var ~ctx ctx.local v + let scope = Node.find_fundec man.node + let find v = get_var ~man man.local v end in let module I = ValueDomain.ValueInvariant (Arg) in @@ -1226,7 +1226,7 @@ struct in let priv_vars = if keep_global then - Priv.invariant_vars ask (priv_getg ctx.global) ctx.local + Priv.invariant_vars ask (priv_getg man.global) man.local else [] in @@ -1258,24 +1258,24 @@ struct ) context.lvals Invariant.none ) - let query_invariant ctx context = + let query_invariant man context = if GobConfig.get_bool "ana.base.invariant.enabled" then - query_invariant ctx context + query_invariant man context else Invariant.none - let query_invariant_global ctx g = + let query_invariant_global man g = if GobConfig.get_bool "ana.base.invariant.enabled" && GobConfig.get_bool "ana.base.invariant.global" then ( (* Currently these global invariants are only sound with earlyglobs enabled for both single- and multi-threaded programs. Otherwise, the values of globals in single-threaded mode are not accounted for. They are also made sound without earlyglobs using the multithreaded mode ghost variable. *) match g with | `Left g' -> (* priv *) - let inv = Priv.invariant_global (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) g' in + let inv = Priv.invariant_global (Analyses.ask_of_man man) (priv_getg man.global) g' in if get_bool "exp.earlyglobs" then inv else ( - if ctx.ask (GhostVarAvailable Multithreaded) then ( + if man.ask (GhostVarAvailable Multithreaded) then ( let var = WitnessGhost.to_varinfo Multithreaded in Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) ) @@ -1298,7 +1298,7 @@ struct For now we return true if the expression contains a shift left. *) (* TODO: deduplicate https://github.com/goblint/analyzer/pull/1297#discussion_r1477804502 *) - let rec exp_may_signed_overflow ctx exp = + let rec exp_may_signed_overflow man exp = let res = match Cilfacade.get_ikind_exp exp with | exception (Cilfacade.TypeOfError _) (* Cilfacade.typeOf *) | exception (Invalid_argument _) -> (* get_ikind *) @@ -1306,7 +1306,7 @@ struct | ik -> let checkDiv e1 e2 = let binop = (GobOption.map2 Z.div )in - match ctx.ask (EvalInt e1), ctx.ask (EvalInt e2) with + match man.ask (EvalInt e1), man.ask (EvalInt e2) with | `Bot, _ -> false | _, `Bot -> false | `Lifted i1, `Lifted i2 -> @@ -1325,7 +1325,7 @@ struct | _ -> true )) | _ -> true in let checkBinop e1 e2 binop = - match ctx.ask (EvalInt e1), ctx.ask (EvalInt e2) with + match man.ask (EvalInt e1), man.ask (EvalInt e2) with | `Bot, _ -> false | _, `Bot -> false | `Lifted i1, `Lifted i2 -> @@ -1340,7 +1340,7 @@ struct | _ -> true) | _ -> true in let checkPredicate e pred = - match ctx.ask (EvalInt e) with + match man.ask (EvalInt e) with | `Bot -> false | `Lifted i -> (let (min_ik, _) = IntDomain.Size.range ik in @@ -1360,7 +1360,7 @@ struct | Imag e | SizeOfE e | AlignOfE e - | CastE (_, e) -> exp_may_signed_overflow ctx e + | CastE (_, e) -> exp_may_signed_overflow man e | UnOp (unop, e, _) -> (* check if the current operation causes a signed overflow *) begin match unop with @@ -1370,7 +1370,7 @@ struct | BNot|LNot -> false end (* look for overflow in subexpression *) - || exp_may_signed_overflow ctx e + || exp_may_signed_overflow man e | BinOp (binop, e1, e2, _) -> (* check if the current operation causes a signed overflow *) (Cil.isSigned ik && begin match binop with @@ -1385,38 +1385,38 @@ struct (* Shiftlt can cause overflow and also undefined behaviour in case the second operand is non-positive*) | Shiftlt -> true end) (* look for overflow in subexpression *) - || exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 + || exp_may_signed_overflow man e1 || exp_may_signed_overflow man e2 | Question (e1, e2, e3, _) -> (* does not result in overflow in C *) - exp_may_signed_overflow ctx e1 || exp_may_signed_overflow ctx e2 || exp_may_signed_overflow ctx e3 + exp_may_signed_overflow man e1 || exp_may_signed_overflow man e2 || exp_may_signed_overflow man e3 | Lval lval | AddrOf lval - | StartOf lval -> lval_may_signed_overflow ctx lval + | StartOf lval -> lval_may_signed_overflow man lval in if M.tracing then M.trace "signed_overflow" "base exp_may_signed_overflow %a. Result = %b" d_plainexp exp res; res - and lval_may_signed_overflow ctx (lval : lval) = + and lval_may_signed_overflow man (lval : lval) = let (host, offset) = lval in let host_may_signed_overflow = function | Var v -> false - | Mem e -> exp_may_signed_overflow ctx e + | Mem e -> exp_may_signed_overflow man e in let rec offset_may_signed_overflow = function | NoOffset -> false - | Index (e, o) -> exp_may_signed_overflow ctx e || offset_may_signed_overflow o + | Index (e, o) -> exp_may_signed_overflow man e || offset_may_signed_overflow o | Field (f, o) -> offset_may_signed_overflow o in host_may_signed_overflow host || offset_may_signed_overflow offset - let query ctx (type a) (q: a Q.t): a Q.result = + let query man (type a) (q: a Q.t): a Q.result = match q with | Q.EvalFunvar e -> - eval_funvar ctx e + eval_funvar man e | Q.EvalJumpBuf e -> - begin match eval_rv_address ~ctx ctx.local e with + begin match eval_rv_address ~man man.local e with | Address jmp_buf -> if AD.mem Addr.UnknownPtr jmp_buf then M.warn ~category:Imprecise "Jump buffer %a may contain unknown pointers." d_exp e; - begin match get ~ctx ~top:(VD.bot ()) ctx.local jmp_buf None with + begin match get ~man ~top:(VD.bot ()) man.local jmp_buf None with | JmpBuf (x, copied) -> if copied then M.warn ~category:(Behavior (Undefined Other)) "The jump buffer %a contains values that were copied here instead of being set by setjmp. This is Undefined Behavior." d_exp e; @@ -1433,12 +1433,12 @@ struct JmpBufDomain.JmpBufSet.top () end | Q.EvalInt e -> - query_evalint ~ctx ctx.local e + query_evalint ~man man.local e | Q.EvalMutexAttr e -> begin - match eval_rv_address ~ctx ctx.local e with + match eval_rv_address ~man man.local e with | Address a -> let default = `Lifted MutexAttrDomain.MutexKind.NonRec in (* Goblint assumption *) - begin match get ~ctx ~top:(MutexAttr default) ctx.local a None with (* ~top corresponds to default NULL with assume_top *) + begin match get ~man ~top:(MutexAttr default) man.local a None with (* ~top corresponds to default NULL with assume_top *) | MutexAttr a -> a | Bot -> default (* corresponds to default NULL with assume_none *) | _ -> MutexAttrDomain.top () @@ -1446,7 +1446,7 @@ struct | _ -> MutexAttrDomain.top () end | Q.EvalLength e -> begin - match eval_rv_address ~ctx ctx.local e with + match eval_rv_address ~man man.local e with | Address a -> let slen = Seq.map String.length (List.to_seq (AD.to_string a)) in let lenOf = function @@ -1461,16 +1461,16 @@ struct | _ -> Queries.Result.top q end | Q.EvalValue e -> - eval_rv ~ctx ctx.local e + eval_rv ~man man.local e | Q.BlobSize {exp = e; base_address = from_base_addr} -> begin - let p = eval_rv_address ~ctx ctx.local e in + let p = eval_rv_address ~man man.local e in (* ignore @@ printf "BlobSize %a MayPointTo %a\n" d_plainexp e VD.pretty p; *) match p with | Address a -> (* If there's a non-heap var or an offset in the lval set, we answer with bottom *) (* If we're asking for the BlobSize from the base address, then don't check for offsets => we want to avoid getting bot *) if AD.exists (function - | Addr (v,o) -> is_not_alloc_var ctx v || (if not from_base_addr then o <> `NoOffset else false) + | Addr (v,o) -> is_not_alloc_var man v || (if not from_base_addr then o <> `NoOffset else false) | _ -> false) a then Queries.Result.bot q else ( @@ -1482,12 +1482,12 @@ struct else a in - let r = get ~ctx ~full:true ctx.local a None in + let r = get ~man ~full:true man.local a None in (* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *) (match r with | Array a -> (* unroll into array for Calloc calls *) - (match ValueDomain.CArrays.get (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) a (None, (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero)) with + (match ValueDomain.CArrays.get (Queries.to_value_domain_ask (Analyses.ask_of_man man)) a (None, (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero)) with | Blob (_,s,_) -> `Lifted s | _ -> Queries.Result.top q ) @@ -1497,14 +1497,14 @@ struct | _ -> Queries.Result.top q end | Q.MayPointTo e -> begin - match eval_rv_address ~ctx ctx.local e with + match eval_rv_address ~man man.local e with | Address a -> a | Bot -> Queries.Result.bot q (* TODO: remove *) | Int i -> AD.of_int i | _ -> Queries.Result.top q end | Q.EvalThread e -> begin - let v = eval_rv ~ctx ctx.local e in + let v = eval_rv ~man man.local e in (* ignore (Pretty.eprintf "evalthread %a (%a): %a" d_exp e d_plainexp e VD.pretty v); *) match v with | Thread a -> a @@ -1512,12 +1512,12 @@ struct | _ -> Queries.Result.top q end | Q.ReachableFrom e -> begin - match eval_rv_address ~ctx ctx.local e with + match eval_rv_address ~man man.local e with | Top -> Queries.Result.top q | Bot -> Queries.Result.bot q (* TODO: remove *) | Address a -> let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe: TODO why? *) - let addrs = reachable_vars ~ctx ctx.local [a'] in + let addrs = reachable_vars ~man man.local [a'] in let addrs' = List.fold_left (AD.join) (AD.empty ()) addrs in if AD.may_be_unknown a then AD.add UnknownPtr addrs' (* add unknown back *) @@ -1532,17 +1532,17 @@ struct | _ -> AD.empty () end | Q.ReachableUkTypes e -> begin - match eval_rv_address ~ctx ctx.local e with + match eval_rv_address ~man man.local e with | Top -> Queries.Result.top q | Bot -> Queries.Result.bot q (* TODO: remove *) | Address a when AD.is_top a || AD.mem Addr.UnknownPtr a -> Q.TS.top () | Address a -> - reachable_top_pointers_types ctx a + reachable_top_pointers_types man a | _ -> Q.TS.empty () end | Q.EvalStr e -> begin - match eval_rv_address ~ctx ctx.local e with + match eval_rv_address ~man man.local e with (* exactly one string in the set (works for assignments of string constants) *) | Address a when List.compare_length_with (AD.to_string a) 1 = 0 -> (* exactly one string *) `Lifted (List.hd (AD.to_string a)) @@ -1568,16 +1568,16 @@ struct (* ignore @@ printf "EvalStr Unknown: %a -> %s\n" d_plainexp e (VD.short 80 x); *) Queries.Result.top q end - | Q.IsMultiple v -> WeakUpdates.mem v ctx.local.weak || + | Q.IsMultiple v -> WeakUpdates.mem v man.local.weak || (hasAttribute "thread" v.vattr && v.vaddrof) (* thread-local variables if they have their address taken, as one could then compare several such variables *) | Q.IterSysVars (vq, vf) -> let vf' x = vf (Obj.repr (V.priv x)) in - Priv.iter_sys_vars (priv_getg ctx.global) vq vf' - | Q.Invariant context -> query_invariant ctx context + Priv.iter_sys_vars (priv_getg man.global) vq vf' + | Q.Invariant context -> query_invariant man context | Q.InvariantGlobal g -> let g: V.t = Obj.obj g in - query_invariant_global ctx g - | Q.MaySignedOverflow e -> (let res = exp_may_signed_overflow ctx e in + query_invariant_global man g + | Q.MaySignedOverflow e -> (let res = exp_may_signed_overflow man e in if M.tracing then M.trace "signed_overflow" "base exp_may_signed_overflow %a. Result = %b" d_plainexp e res; res ) | _ -> Q.Result.top q @@ -1610,7 +1610,7 @@ struct (** [set st addr val] returns a state where [addr] is set to [val] * it is always ok to put None for lval_raw and rval_raw, this amounts to not using/maintaining * precise information about arrays. *) - let set ~(ctx: _ ctx) ?(invariant=false) ?(blob_destructive=false) ?lval_raw ?rval_raw ?t_override (st: store) (lval: AD.t) (lval_type: Cil.typ) (value: value) : store = + let set ~(man: _ man) ?(invariant=false) ?(blob_destructive=false) ?lval_raw ?rval_raw ?t_override (st: store) (lval: AD.t) (lval_type: Cil.typ) (value: value) : store = let update_variable x t y z = if M.tracing then M.tracel "set" ~var:x.vname "update_variable: start '%s' '%a'\nto\n%a" x.vname VD.pretty y CPA.pretty z; let r = update_variable x t y z in (* refers to defintion that is outside of set *) @@ -1623,12 +1623,12 @@ struct (* Updating a single varinfo*offset pair. NB! This function's type does * not include the flag. *) let update_one_addr (x, offs) (st: store): store = - let ask = Analyses.ask_of_ctx ctx in + let ask = Analyses.ask_of_man man in let cil_offset = Offs.to_cil_offset offs in let t = match t_override with | Some t -> t | None -> - if ctx.ask (Q.IsAllocVar x) then + if man.ask (Q.IsAllocVar x) then (* the vtype of heap vars will be TVoid, so we need to trust the pointer we got to this to be of the right type *) (* i.e. use the static type of the pointer here *) lval_type @@ -1670,18 +1670,18 @@ struct * side-effects here, but the code still distinguishes these cases. *) if (!earlyglobs || ThreadFlag.has_ever_been_multi ask) && is_global ask x then begin if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: update a global var '%s' ..." x.vname; - let priv_getg = priv_getg ctx.global in + let priv_getg = priv_getg man.global in (* Optimization to avoid evaluating integer values when setting them. The case when invariant = true requires the old_value to be sound for the meet. Allocated blocks are representend by Blobs with additional information, so they need to be looked-up. *) - let old_value = if not invariant && Cil.isIntegralType x.vtype && not (ctx.ask (IsAllocVar x)) && offs = `NoOffset then begin + let old_value = if not invariant && Cil.isIntegralType x.vtype && not (man.ask (IsAllocVar x)) && offs = `NoOffset then begin VD.bot_value ~varAttr:x.vattr lval_type end else Priv.read_global ask priv_getg st x in let new_value = update_offset old_value in if M.tracing then M.tracel "set" "update_offset %a -> %a" VD.pretty old_value VD.pretty new_value; - let r = Priv.write_global ~invariant ask priv_getg (priv_sideg ctx.sideg) st x new_value in + let r = Priv.write_global ~invariant ask priv_getg (priv_sideg man.sideg) st x new_value in if M.tracing then M.tracel "set" ~var:x.vname "update_one_addr: updated a global var '%s' \nstate:%a" x.vname D.pretty r; r end else begin @@ -1725,11 +1725,11 @@ struct VD.affect_move (Queries.to_value_domain_ask a) v x (fun x -> None) else let patched_ask = - (* 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. *) + (* The usual recursion trick for man. *) + (* Must change man used by ask to also use new st (not man.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 + let rec man' asked = + { man with ask = (fun (type a) (q: a Queries.t) -> query' asked q) ; local = st } @@ -1739,10 +1739,10 @@ struct Queries.Result.top q (* query cycle *) else ( let asked' = Queries.Set.add anyq asked in - query (ctx' asked') q + query (man' asked') q ) in - Analyses.ask_of_ctx (ctx' Queries.Set.empty) + Analyses.ask_of_man (man' Queries.Set.empty) in let moved_by = fun x -> Some 0 in (* this is ok, the information is not provided if it *) (* TODO: why does affect_move need general ask (of any query) instead of eval_exp? *) @@ -1781,10 +1781,10 @@ struct (* if M.tracing then M.tracel "set" ~var:firstvar "set got an exception '%s'" x; *) M.info ~category:Unsound "Assignment to unknown address, assuming no write happened."; st - let set_many ~ctx (st: store) lval_value_list: store = + let set_many ~man (st: store) lval_value_list: store = (* Maybe this can be done with a simple fold *) let f (acc: store) ((lval:AD.t),(typ:Cil.typ),(value:value)): store = - set ~ctx acc lval typ value + set ~man acc lval typ value in (* And fold over the list starting from the store turned wstore: *) List.fold_left f st lval_value_list @@ -1836,12 +1836,12 @@ struct let convert_offset = convert_offset let get_var = get_var - let get ~ctx st addrs exp = get ~ctx st addrs exp - let set ~ctx st lval lval_type ?lval_raw value = set ~ctx ~invariant:true st lval lval_type ?lval_raw value + let get ~man st addrs exp = get ~man st addrs exp + let set ~man st lval lval_type ?lval_raw value = set ~man ~invariant:true st lval lval_type ?lval_raw value let refine_entire_var = true let map_oldval oldval _ = oldval - let eval_rv_lval_refine ~ctx st exp lval = eval_rv ~ctx st (Lval lval) + let eval_rv_lval_refine ~man st exp lval = eval_rv ~man st (Lval lval) let id_meet_down ~old ~c = ID.meet old c let fd_meet_down ~old ~c = FD.meet old c @@ -1854,17 +1854,17 @@ struct let invariant = Invariant.invariant - let set_savetop ~ctx ?lval_raw ?rval_raw st adr lval_t v : store = + let set_savetop ~man ?lval_raw ?rval_raw st adr lval_t v : store = if M.tracing then M.tracel "set" "savetop %a %a %a" AD.pretty adr d_type lval_t VD.pretty v; match v with - | Top -> set ~ctx st adr lval_t (VD.top_value (AD.type_of adr)) ?lval_raw ?rval_raw - | v -> set ~ctx st adr lval_t v ?lval_raw ?rval_raw + | Top -> set ~man st adr lval_t (VD.top_value (AD.type_of adr)) ?lval_raw ?rval_raw + | v -> set ~man st adr lval_t v ?lval_raw ?rval_raw (************************************************************************** * Simple defs for the transfer functions **************************************************************************) - let assign ctx (lval:lval) (rval:exp):store = + let assign man (lval:lval) (rval:exp):store = let lval_t = Cilfacade.typeOfLval lval in let char_array_hack () = let rec split_offset = function @@ -1901,15 +1901,15 @@ struct | _ -> () in char_array_hack (); - let rval_val = eval_rv ~ctx ctx.local rval in + let rval_val = eval_rv ~man man.local rval in let rval_val = VD.mark_jmpbufs_as_copied rval_val in - let lval_val = eval_lv ~ctx ctx.local lval in + let lval_val = eval_lv ~man man.local lval in (* let sofa = AD.short 80 lval_val^" = "^VD.short 80 rval_val in *) (* M.debug ~category:Analyzer @@ sprint ~width:max_int @@ dprintf "%a = %a\n%s" d_plainlval lval d_plainexp rval sofa; *) let not_local xs = let not_local x = match Addr.to_var_may x with - | Some x -> is_global (Analyses.ask_of_ctx ctx) x + | Some x -> is_global (Analyses.ask_of_man man) x | None -> x = Addr.UnknownPtr in AD.is_top xs || AD.exists not_local xs @@ -1923,7 +1923,7 @@ struct in let vars = AD.fold find_fps adrs [] in (* filter_map from AD to list *) let funs = Seq.filter (fun x -> isFunctionType x.vtype)@@ List.to_seq vars in - Seq.iter (fun x -> ctx.spawn None x []) funs + Seq.iter (fun x -> man.spawn None x []) funs | _ -> () ); match lval with (* this section ensure global variables contain bottom values of the proper type before setting them *) @@ -1935,7 +1935,7 @@ struct assert (offs = NoOffset); VD.Bot end else - eval_rv_keep_bot ~ctx ctx.local (Lval (Var v, NoOffset)) + eval_rv_keep_bot ~man man.local (Lval (Var v, NoOffset)) in begin match current_val with | Bot -> (* current value is VD Bot *) @@ -1944,29 +1944,29 @@ struct let t = v.vtype in let iv = VD.bot_value ~varAttr:v.vattr t in (* correct bottom value for top level variable *) if M.tracing then M.tracel "set" "init bot value (%a): %a" d_plaintype t VD.pretty iv; - let nv = VD.update_offset (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) iv offs rval_val (Some (Lval lval)) lval t in (* do desired update to value *) - set_savetop ~ctx ctx.local (AD.of_var v) lval_t nv ~lval_raw:lval ~rval_raw:rval (* set top-level variable to updated value *) + let nv = VD.update_offset (Queries.to_value_domain_ask (Analyses.ask_of_man man)) iv offs rval_val (Some (Lval lval)) lval t in (* do desired update to value *) + set_savetop ~man man.local (AD.of_var v) lval_t nv ~lval_raw:lval ~rval_raw:rval (* set top-level variable to updated value *) | _ -> - set_savetop ~ctx ctx.local lval_val lval_t rval_val ~lval_raw:lval ~rval_raw:rval + set_savetop ~man man.local lval_val lval_t rval_val ~lval_raw:lval ~rval_raw:rval end | _ -> - set_savetop ~ctx ctx.local lval_val lval_t rval_val ~lval_raw:lval ~rval_raw:rval + set_savetop ~man man.local lval_val lval_t rval_val ~lval_raw:lval ~rval_raw:rval end | _ -> - set_savetop ~ctx ctx.local lval_val lval_t rval_val ~lval_raw:lval ~rval_raw:rval + set_savetop ~man man.local lval_val lval_t rval_val ~lval_raw:lval ~rval_raw:rval - let branch ctx (exp:exp) (tv:bool) : store = - let valu = eval_rv ~ctx ctx.local exp in + let branch man (exp:exp) (tv:bool) : store = + let valu = eval_rv ~man man.local exp in let refine () = - let res = invariant ctx ctx.local exp tv in - if M.tracing then M.tracec "branch" "EqualSet result for expression %a is %a" d_exp exp Queries.ES.pretty (ctx.ask (Queries.EqualSet exp)); - if M.tracing then M.tracec "branch" "CondVars result for expression %a is %a" d_exp exp Queries.ES.pretty (ctx.ask (Queries.CondVars exp)); + let res = invariant man man.local exp tv in + if M.tracing then M.tracec "branch" "EqualSet result for expression %a is %a" d_exp exp Queries.ES.pretty (man.ask (Queries.EqualSet exp)); + if M.tracing then M.tracec "branch" "CondVars result for expression %a is %a" d_exp exp Queries.ES.pretty (man.ask (Queries.CondVars exp)); if M.tracing then M.traceu "branch" "Invariant enforced!"; - match ctx.ask (Queries.CondVars exp) with + match man.ask (Queries.CondVars exp) with | s when Queries.ES.cardinal s = 1 -> let e = Queries.ES.choose s in - invariant ctx res e tv + invariant man res e tv | _ -> res in if M.tracing then M.traceli "branch" ~subsys:["invariant"] "Evaluating branch for expression %a with value %a" d_exp exp VD.pretty valu; @@ -2001,28 +2001,28 @@ struct For example, 50-juliet/08-CWE570_Expression_Always_False__02. *) refine () - let body ctx f = + let body man f = (* First we create a variable-initvalue pair for each variable *) let init_var v = (AD.of_var v, v.vtype, VD.init_value ~varAttr:v.vattr v.vtype) in (* Apply it to all the locals and then assign them all *) let inits = List.map init_var f.slocals in - set_many ~ctx ctx.local inits + set_many ~man man.local inits - let return ctx exp fundec: store = + let return man exp fundec: store = if Cil.hasAttribute "noreturn" fundec.svar.vattr then M.warn ~category:(Behavior (Undefined Other)) "Function declared 'noreturn' could return"; - let ask = Analyses.ask_of_ctx ctx in - let st: store = ctx.local in + let ask = Analyses.ask_of_man man in + let st: store = man.local in match fundec.svar.vname with | "__goblint_dummy_init" -> if M.tracing then M.trace "init" "dummy init: %a" D.pretty st; - publish_all ctx `Init; + publish_all man `Init; (* otherfun uses __goblint_dummy_init, where we can properly side effect global initialization *) (* TODO: move into sync `Init *) - Priv.enter_multithreaded ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st + Priv.enter_multithreaded ask (priv_getg man.global) (priv_sideg man.sideg) st | _ -> let locals = List.filter (fun v -> not (WeakUpdates.mem v st.weak)) (fundec.sformals @ fundec.slocals) in - let nst_part = rem_many_partitioning (Queries.to_value_domain_ask ask) ctx.local locals in + let nst_part = rem_many_partitioning (Queries.to_value_domain_ask ask) man.local locals in let nst: store = rem_many ask nst_part locals in match exp with | None -> nst @@ -2031,65 +2031,65 @@ struct | TVoid _ -> M.warn ~category:M.Category.Program "Returning a value from a void function"; assert false | ret -> ret in - let rv = eval_rv ~ctx ctx.local exp in - let st' = set ~ctx ~t_override nst (return_var ()) t_override rv in + let rv = eval_rv ~man man.local exp in + let st' = set ~man ~t_override nst (return_var ()) t_override rv in match ThreadId.get_current ask with | `Lifted tid when ThreadReturn.is_current ask -> (* Evaluate exp and cast the resulting value to the void-pointer-type. Casting to the right type here avoids precision loss on joins. *) let rv = VD.cast ~torg:(Cilfacade.typeOf exp) Cil.voidPtrType rv in - ctx.sideg (V.thread tid) (G.create_thread rv); - Priv.thread_return ask (priv_getg ctx.global) (priv_sideg ctx.sideg) tid st' + man.sideg (V.thread tid) (G.create_thread rv); + Priv.thread_return ask (priv_getg man.global) (priv_sideg man.sideg) tid st' | _ -> st' - let vdecl ctx (v:varinfo) = + let vdecl man (v:varinfo) = if not (Cil.isArrayType v.vtype) then - ctx.local + man.local else - let lval = eval_lv ~ctx ctx.local (Var v, NoOffset) in - let current_value = eval_rv ~ctx ctx.local (Lval (Var v, NoOffset)) in - let new_value = VD.update_array_lengths (eval_rv ~ctx ctx.local) current_value v.vtype in - set ~ctx ctx.local lval v.vtype new_value + let lval = eval_lv ~man man.local (Var v, NoOffset) in + let current_value = eval_rv ~man man.local (Lval (Var v, NoOffset)) in + let new_value = VD.update_array_lengths (eval_rv ~man man.local) current_value v.vtype in + set ~man man.local lval v.vtype new_value (************************************************************************** * Function calls **************************************************************************) (** From a list of expressions, collect a list of addresses that they might point to, or contain pointers to. *) - let collect_funargs ~ctx ?(warn=false) (st:store) (exps: exp list) = - let ask = Analyses.ask_of_ctx ctx in + let collect_funargs ~man ?(warn=false) (st:store) (exps: exp list) = + let ask = Analyses.ask_of_man man in let do_exp e = - let immediately_reachable = reachable_from_value ask (eval_rv ~ctx st e) (Cilfacade.typeOf e) (CilType.Exp.show e) in - reachable_vars ~ctx st [immediately_reachable] + let immediately_reachable = reachable_from_value ask (eval_rv ~man st e) (Cilfacade.typeOf e) (CilType.Exp.show e) in + reachable_vars ~man st [immediately_reachable] in List.concat_map do_exp exps - let collect_invalidate ~deep ~ctx ?(warn=false) (st:store) (exps: exp list) = + let collect_invalidate ~deep ~man ?(warn=false) (st:store) (exps: exp list) = if deep then - collect_funargs ~ctx ~warn st exps + collect_funargs ~man ~warn st exps else ( - let mpt e = match eval_rv_address ~ctx st e with + let mpt e = match eval_rv_address ~man st e with | Address a -> AD.remove NullPtr a | _ -> AD.empty () in List.map mpt exps ) - let invalidate ~(must: bool) ?(deep=true) ~ctx (st:store) (exps: exp list): store = + let invalidate ~(must: bool) ?(deep=true) ~man (st:store) (exps: exp list): store = if M.tracing && exps <> [] then M.tracel "invalidate" "Will invalidate expressions [%a]" (d_list ", " d_plainexp) exps; if exps <> [] then M.info ~category:Imprecise "Invalidating expressions: %a" (d_list ", " d_exp) exps; (* To invalidate a single address, we create a pair with its corresponding * top value. *) let invalidate_address st a = let t = try AD.type_of a with Not_found -> voidType in (* TODO: why is this called with empty a to begin with? *) - let v = get ~ctx st a None in (* None here is ok, just causes us to be a bit less precise *) - let nv = VD.invalidate_value (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) t v in + let v = get ~man st a None in (* None here is ok, just causes us to be a bit less precise *) + let nv = VD.invalidate_value (Queries.to_value_domain_ask (Analyses.ask_of_man man)) t v in (a, t, nv) in (* We define the function that invalidates all the values that an address * expression e may point to *) let invalidate_exp exps = - let args = collect_invalidate ~deep ~ctx ~warn:true st exps in + let args = collect_invalidate ~deep ~man ~warn:true st exps in List.map (invalidate_address st) args in let invalids = invalidate_exp exps in @@ -2104,7 +2104,7 @@ struct ); (* copied from set_many *) let f (acc: store) ((lval:AD.t),(typ:Cil.typ),(value:value)): store = - let acc' = set ~ctx acc lval typ value in + let acc' = set ~man acc lval typ value in if must then acc' else @@ -2113,11 +2113,11 @@ struct List.fold_left f st invalids' - let make_entry ?(thread=false) (ctx:(D.t, G.t, C.t, V.t) Analyses.ctx) fundec args: D.t = - let ask = Analyses.ask_of_ctx ctx in - let st: store = ctx.local in + let make_entry ?(thread=false) (man:(D.t, G.t, C.t, V.t) Analyses.man) fundec args: D.t = + let ask = Analyses.ask_of_man man in + let st: store = man.local in (* Evaluate the arguments. *) - let vals = List.map (eval_rv ~ctx st) args in + let vals = List.map (eval_rv ~man st) args in (* generate the entry states *) (* If we need the globals, add them *) (* TODO: make this is_private PrivParam dependent? PerMutexOplusPriv should keep *) @@ -2128,12 +2128,12 @@ struct sync `Thread doesn't help us here, it's not specific to entering multithreaded mode. EnterMultithreaded events only execute after threadenter and threadspawn. *) if not (ThreadFlag.has_ever_been_multi ask) then - ignore (Priv.enter_multithreaded ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st); + ignore (Priv.enter_multithreaded ask (priv_getg man.global) (priv_sideg man.sideg) st); Priv.threadenter ask st ) else (* use is_global to account for values that became globals because they were saved into global variables *) let globals = CPA.filter (fun k v -> is_global ask k) st.cpa in - (* let new_cpa = if !earlyglobs || ThreadFlag.is_multi ctx.ask then CPA.filter (fun k v -> is_private ctx.ask ctx.local k) globals else globals in *) + (* let new_cpa = if !earlyglobs || ThreadFlag.is_multi man.ask then CPA.filter (fun k v -> is_private man.ask man.local k) globals else globals in *) let new_cpa = globals in {st with cpa = new_cpa} in @@ -2142,7 +2142,7 @@ struct add_to_array_map fundec pa; let new_cpa = CPA.add_list pa st'.cpa in (* List of reachable variables *) - let reachable = List.concat_map AD.to_var_may (reachable_vars ~ctx st (get_ptrs vals)) in + let reachable = List.concat_map AD.to_var_may (reachable_vars ~man st (get_ptrs vals)) in let reachable = List.filter (fun v -> CPA.mem v st.cpa) reachable in let new_cpa = CPA.add_list_fun reachable (fun v -> CPA.find v st.cpa) new_cpa in @@ -2156,12 +2156,12 @@ struct let new_weak = WeakUpdates.join st.weak (WeakUpdates.of_list reachable_other_copies) in {st' with cpa = new_cpa; weak = new_weak} - let enter ctx lval fn args : (D.t * D.t) list = - [ctx.local, make_entry ctx fn args] + let enter man lval fn args : (D.t * D.t) list = + [man.local, make_entry man fn args] - let forkfun (ctx:(D.t, G.t, C.t, V.t) Analyses.ctx) (lv: lval option) (f: varinfo) (args: exp list) : (lval option * varinfo * exp list * bool) list = + let forkfun (man:(D.t, G.t, C.t, V.t) Analyses.man) (lv: lval option) (f: varinfo) (args: exp list) : (lval option * varinfo * exp list * bool) list = let create_thread ~multiple lval arg v = try (* try to get function declaration *) @@ -2192,9 +2192,9 @@ struct (* handling thread creations *) | ThreadCreate { thread = id; start_routine = start; arg = ptc_arg; multiple }, _ -> begin (* extra sync so that we do not analyze new threads with bottom global invariant *) - publish_all ctx `Thread; + publish_all man `Thread; (* Collect the threads. *) - let start_addr = eval_tv ~ctx ctx.local start in + let start_addr = eval_tv ~man man.local start in let start_funvars = AD.to_var_may start_addr in let start_funvars_with_unknown = if AD.mem Addr.UnknownPtr start_addr then @@ -2207,23 +2207,23 @@ struct | _, _ -> let shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = false } args in let deep_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = true } args in - let shallow_flist = collect_invalidate ~deep:false ~ctx ctx.local shallow_args in - let deep_flist = collect_invalidate ~deep:true ~ctx ctx.local deep_args in + let shallow_flist = collect_invalidate ~deep:false ~man man.local shallow_args in + let deep_flist = collect_invalidate ~deep:true ~man man.local deep_args in let flist = shallow_flist @ deep_flist in let addrs = List.concat_map AD.to_var_may flist in if addrs <> [] then M.debug ~category:Analyzer "Spawning non-unique functions from unknown function: %a" (d_list ", " CilType.Varinfo.pretty) addrs; List.filter_map (create_thread ~multiple:true None None) addrs - let assert_fn ctx e refine = + let assert_fn man e refine = (* make the state meet the assertion in the rest of the code *) - if not refine then ctx.local else begin - let newst = invariant ctx ctx.local e true in + if not refine then man.local else begin + let newst = invariant man man.local e true in (* if check_assert e newst <> `Lifted true then M.warn ~category:Assert ~msg:("Invariant \"" ^ expr ^ "\" does not stick.") (); *) newst end - let special_unknown_invalidate ctx f args = + let special_unknown_invalidate man f args = (if CilType.Varinfo.equal f dummyFunDec.svar then M.warn ~category:Imprecise ~tags:[Category Call] "Unknown function ptr called"); let desc = LF.find f in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in @@ -2244,19 +2244,19 @@ struct in (* TODO: what about escaped local variables? *) (* invalidate arguments and non-static globals for unknown functions *) - let st' = invalidate ~must:false ~deep:false ~ctx ctx.local shallow_addrs in - invalidate ~must:false ~deep:true ~ctx st' deep_addrs + let st' = invalidate ~must:false ~deep:false ~man man.local shallow_addrs in + invalidate ~must:false ~deep:true ~man st' deep_addrs - let check_invalid_mem_dealloc ctx special_fn ptr = + let check_invalid_mem_dealloc man special_fn ptr = let has_non_heap_var = AD.exists (function - | Addr (v,_) -> is_not_heap_alloc_var ctx v + | Addr (v,_) -> is_not_heap_alloc_var man v | _ -> false) in let has_non_zero_offset = AD.exists (function | Addr (_,o) -> Offs.cmp_zero_offset o <> `MustZero | _ -> false) in - match eval_rv_address ~ctx ctx.local ptr with + match eval_rv_address ~man man.local ptr with | Address a -> if AD.is_top a then ( AnalysisStateUtil.set_mem_safety_flag InvalidFree; @@ -2272,16 +2272,16 @@ struct AnalysisStateUtil.set_mem_safety_flag InvalidFree; M.warn ~category:(Behavior (Undefined InvalidMemoryDeallocation)) ~tags:[CWE 590] "Pointer %a in function %s doesn't evaluate to a valid address. Invalid memory deallocation may occur" d_exp ptr special_fn.vname - let points_to_heap_only ctx ptr = - match ctx.ask (Queries.MayPointTo ptr) with + let points_to_heap_only man ptr = + match man.ask (Queries.MayPointTo ptr) with | a when not (Queries.AD.is_top a)-> Queries.AD.for_all (function - | Addr (v, _) -> ctx.ask (Queries.IsHeapVar v) + | Addr (v, _) -> man.ask (Queries.IsHeapVar v) | _ -> false ) a | _ -> false - let get_size_of_ptr_target ctx ptr = + let get_size_of_ptr_target man ptr = let intdom_of_int x = ID.of_int (Cilfacade.ptrdiff_ikind ()) (Z.of_int x) in @@ -2289,11 +2289,11 @@ struct let typ_size_in_bytes = (bitsSizeOf typ) / 8 in intdom_of_int typ_size_in_bytes in - if points_to_heap_only ctx ptr then + if points_to_heap_only man ptr then (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) - ctx.ask (Queries.BlobSize {exp = ptr; base_address = true}) + man.ask (Queries.BlobSize {exp = ptr; base_address = true}) else - match ctx.ask (Queries.MayPointTo ptr) with + match man.ask (Queries.MayPointTo ptr) with | a when not (Queries.AD.is_top a) -> let pts_list = Queries.AD.elements a in let pts_elems_to_sizes (addr: Queries.AD.elt) = @@ -2302,7 +2302,7 @@ struct begin match v.vtype with | TArray (item_typ, _, _) -> let item_typ_size_in_bytes = size_of_type_in_bytes item_typ in - begin match ctx.ask (Queries.EvalLength ptr) with + begin match man.ask (Queries.EvalLength ptr) with | `Lifted arr_len -> let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in begin @@ -2331,26 +2331,26 @@ struct (M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; `Top) - let special ctx (lv:lval option) (f: varinfo) (args: exp list) = + let special man (lv:lval option) (f: varinfo) (args: exp list) = let invalidate_ret_lv st = match lv with | Some lv -> if M.tracing then M.tracel "invalidate" "Invalidating lhs %a for function call %s" d_plainlval lv f.vname; - invalidate ~must:true ~deep:false ~ctx st [Cil.mkAddrOrStartOf lv] + invalidate ~must:true ~deep:false ~man st [Cil.mkAddrOrStartOf lv] | None -> st in let addr_type_of_exp exp = let lval = mkMem ~addr:(Cil.stripCasts exp) ~off:NoOffset in - let addr = eval_lv ~ctx ctx.local lval in + let addr = eval_lv ~man man.local lval in (addr, AD.type_of addr) in - let forks = forkfun ctx lv f args in + let forks = forkfun man lv f args in if M.tracing then if not (List.is_empty forks) then M.tracel "spawn" "Base.special %s: spawning functions %a" f.vname (d_list "," CilType.Varinfo.pretty) (List.map BatTuple.Tuple4.second forks); - List.iter (fun (lval, f, args, multiple) -> ctx.spawn ~multiple lval f args) forks; - let st: store = ctx.local in + List.iter (fun (lval, f, args, multiple) -> man.spawn ~multiple lval f args) forks; + let st: store = man.local in let desc = LF.find f in let memory_copying dst src n = - let dest_size = get_size_of_ptr_target ctx dst in - let n_intdom = Option.map_default (fun exp -> ctx.ask (Queries.EvalInt exp)) `Bot n in + let dest_size = get_size_of_ptr_target man dst in + let n_intdom = Option.map_default (fun exp -> man.ask (Queries.EvalInt exp)) `Bot n in let dest_size_equal_n = match dest_size, n_intdom with | `Lifted ds, `Lifted n -> @@ -2366,21 +2366,21 @@ struct in let dest_a, dest_typ = addr_type_of_exp dst in let src_lval = mkMem ~addr:(Cil.stripCasts src) ~off:NoOffset in - let src_typ = eval_lv ~ctx ctx.local src_lval + let src_typ = eval_lv ~man man.local src_lval |> AD.type_of in (* when src and destination type coincide, take value from the source, otherwise use top *) let value = if (typeSig dest_typ = typeSig src_typ) && dest_size_equal_n then let src_cast_lval = mkMem ~addr:(Cilfacade.mkCast ~e:src ~newt:(TPtr (dest_typ, []))) ~off:NoOffset in - eval_rv ~ctx st (Lval src_cast_lval) + eval_rv ~man st (Lval src_cast_lval) else VD.top_value (unrollType dest_typ) in - set ~ctx st dest_a dest_typ value in + set ~man st dest_a dest_typ value in (* for string functions *) let eval_n = function (* if only n characters of a given string are needed, evaluate expression n to an integer option *) | Some n -> - begin match eval_rv ~ctx st n with + begin match eval_rv ~man st n with | Int i -> begin match ID.to_int i with | Some x -> Some (Z.to_int x) @@ -2406,10 +2406,10 @@ struct | _ -> raise (Failure "String function: not an address") in let string_manipulation s1 s2 lv all op_addr op_array = - let s1_v = eval_rv ~ctx st s1 in + let s1_v = eval_rv ~man st s1 in let s1_a = address_from_value s1_v in let s1_typ = AD.type_of s1_a in - let s2_v = eval_rv ~ctx st s2 in + let s2_v = eval_rv ~man st s2 in let s2_a = address_from_value s2_v in let s2_typ = AD.type_of s2_a in (* compute value in string literals domain if s1 and s2 are both string literals *) @@ -2418,68 +2418,68 @@ struct begin match lv, op_addr with | Some lv_val, Some f -> (* when whished types coincide, compute result of operation op_addr, otherwise use top *) - let lv_a = eval_lv ~ctx st lv_val in + let lv_a = eval_lv ~man st lv_val in let lv_typ = Cilfacade.typeOfLval lv_val in if all && typeSig s1_typ = typeSig s2_typ && typeSig s2_typ = typeSig lv_typ then (* all types need to coincide *) - set ~ctx st lv_a lv_typ (f s1_a s2_a) + set ~man st lv_a lv_typ (f s1_a s2_a) else if not all && typeSig s1_typ = typeSig s2_typ then (* only the types of s1 and s2 need to coincide *) - set ~ctx st lv_a lv_typ (f s1_a s2_a) + set ~man st lv_a lv_typ (f s1_a s2_a) else - set ~ctx st lv_a lv_typ (VD.top_value (unrollType lv_typ)) + set ~man st lv_a lv_typ (VD.top_value (unrollType lv_typ)) | _ -> (* check if s1 is potentially a string literal as writing to it would be undefined behavior; then return top *) let _ = AD.string_writing_defined s1_a in - set ~ctx st s1_a s1_typ (VD.top_value (unrollType s1_typ)) + set ~man st s1_a s1_typ (VD.top_value (unrollType s1_typ)) end (* else compute value in array domain *) else let lv_a, lv_typ = match lv with - | Some lv_val -> eval_lv ~ctx st lv_val, Cilfacade.typeOfLval lv_val + | Some lv_val -> eval_lv ~man st lv_val, Cilfacade.typeOfLval lv_val | None -> s1_a, s1_typ in - begin match (get ~ctx st s1_a None), get ~ctx st s2_a None with - | Array array_s1, Array array_s2 -> set ~ctx ~blob_destructive:true st lv_a lv_typ (op_array array_s1 array_s2) + begin match (get ~man st s1_a None), get ~man st s2_a None with + | Array array_s1, Array array_s2 -> set ~man ~blob_destructive:true st lv_a lv_typ (op_array array_s1 array_s2) | Array array_s1, _ when CilType.Typ.equal s2_typ charPtrType -> let s2_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s2_a) in let array_s2 = List.fold_left CArrays.join (CArrays.bot ()) s2_null_bytes in - set ~ctx ~blob_destructive:true st lv_a lv_typ (op_array array_s1 array_s2) + set ~man ~blob_destructive:true st lv_a lv_typ (op_array array_s1 array_s2) | Bot, Array array_s2 -> (* If we have bot inside here, we assume the blob is used as a char array and create one inside *) let ptrdiff_ik = Cilfacade.ptrdiff_ikind () in - let size = ctx.ask (Q.BlobSize {exp = s1; base_address = false}) in + let size = man.ask (Q.BlobSize {exp = s1; base_address = false}) in let s_id = try ValueDomainQueries.ID.unlift (ID.cast_to ptrdiff_ik) size with Failure _ -> ID.top_of ptrdiff_ik in let empty_array = CArrays.make s_id (Int (ID.top_of IChar)) in - set ~ctx st lv_a lv_typ (op_array empty_array array_s2) + set ~man st lv_a lv_typ (op_array empty_array array_s2) | Bot , _ when CilType.Typ.equal s2_typ charPtrType -> (* If we have bot inside here, we assume the blob is used as a char array and create one inside *) let ptrdiff_ik = Cilfacade.ptrdiff_ikind () in - let size = ctx.ask (Q.BlobSize {exp = s1; base_address = false}) in + let size = man.ask (Q.BlobSize {exp = s1; base_address = false}) in let s_id = try ValueDomainQueries.ID.unlift (ID.cast_to ptrdiff_ik) size with Failure _ -> ID.top_of ptrdiff_ik in let empty_array = CArrays.make s_id (Int (ID.top_of IChar)) in let s2_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s2_a) in let array_s2 = List.fold_left CArrays.join (CArrays.bot ()) s2_null_bytes in - set ~ctx st lv_a lv_typ (op_array empty_array array_s2) + set ~man st lv_a lv_typ (op_array empty_array array_s2) | _, Array array_s2 when CilType.Typ.equal s1_typ charPtrType -> (* if s1 is string literal, str(n)cpy and str(n)cat are undefined *) if op_addr = None then (* triggers warning, function only evaluated for side-effects *) let _ = AD.string_writing_defined s1_a in - set ~ctx st s1_a s1_typ (VD.top_value (unrollType s1_typ)) + set ~man st s1_a s1_typ (VD.top_value (unrollType s1_typ)) else let s1_null_bytes = List.map CArrays.to_null_byte_domain (AD.to_string s1_a) in let array_s1 = List.fold_left CArrays.join (CArrays.bot ()) s1_null_bytes in - set ~ctx st lv_a lv_typ (op_array array_s1 array_s2) + set ~man st lv_a lv_typ (op_array array_s1 array_s2) | _ -> - set ~ctx st lv_a lv_typ (VD.top_value (unrollType lv_typ)) + set ~man st lv_a lv_typ (VD.top_value (unrollType lv_typ)) end in let st = match desc.special args, f.vname with | Memset { dest; ch; count; }, _ -> (* TODO: check count *) - let eval_ch = eval_rv ~ctx st ch in + let eval_ch = eval_rv ~man st ch in let dest_a, dest_typ = addr_type_of_exp dest in let value = match eval_ch with @@ -2488,13 +2488,13 @@ struct | _ -> VD.top_value dest_typ in - set ~ctx st dest_a dest_typ value + set ~man st dest_a dest_typ value | Bzero { dest; count; }, _ -> (* TODO: share something with memset special case? *) (* TODO: check count *) let dest_a, dest_typ = addr_type_of_exp dest in let value = VD.zero_init_value dest_typ in - set ~ctx st dest_a dest_typ value + set ~man st dest_a dest_typ value | Memcpy { dest = dst; src; n; }, _ -> (* TODO: use n *) memory_copying dst src (Some n) | Strcpy { dest = dst; src; n }, _ -> string_manipulation dst src None false None (fun ar1 ar2 -> Array (CArrays.string_copy ar1 ar2 (eval_n n))) @@ -2502,9 +2502,9 @@ struct | Strlen s, _ -> begin match lv with | Some lv_val -> - let dest_a = eval_lv ~ctx st lv_val in + let dest_a = eval_lv ~man st lv_val in let dest_typ = Cilfacade.typeOfLval lv_val in - let v = eval_rv ~ctx st s in + let v = eval_rv ~man st s in let a = address_from_value v in let value:value = (* if s string literal, compute strlen in string literals domain *) @@ -2513,11 +2513,11 @@ struct Int (AD.to_string_length a) (* else compute strlen in array domain *) else - begin match get ~ctx st a None with + begin match get ~man st a None with | Array array_s -> Int (CArrays.to_string_length array_s) | _ -> VD.top_value (unrollType dest_typ) end in - set ~ctx st dest_a dest_typ value + set ~man st dest_a dest_typ value | None -> st end | Strstr { haystack; needle }, _ -> @@ -2530,8 +2530,8 @@ struct string_manipulation haystack needle lv true (Some (fun h_a n_a -> Address (AD.substring_extraction h_a n_a))) (fun h_ar n_ar -> match CArrays.substring_extraction h_ar n_ar with | CArrays.IsNotSubstr -> Address (AD.null_ptr) - | CArrays.IsSubstrAtIndex0 -> Address (eval_lv ~ctx st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) - | CArrays.IsMaybeSubstr -> Address (AD.join (eval_lv ~ctx st + | CArrays.IsSubstrAtIndex0 -> Address (eval_lv ~man st (mkMem ~addr:(Cil.stripCasts haystack) ~off:NoOffset)) + | CArrays.IsMaybeSubstr -> Address (AD.join (eval_lv ~man st (mkMem ~addr:(Cil.stripCasts haystack) ~off:(Index (Lazy.force Offset.Index.Exp.any, NoOffset)))) (AD.null_ptr))) | None -> st end @@ -2546,18 +2546,18 @@ struct end | Abort, _ -> raise Deadcode | ThreadExit { ret_val = exp }, _ -> - begin match ThreadId.get_current (Analyses.ask_of_ctx ctx) with + begin match ThreadId.get_current (Analyses.ask_of_man man) with | `Lifted tid -> ( - let rv = eval_rv ~ctx ctx.local exp in - ctx.sideg (V.thread tid) (G.create_thread rv); + let rv = eval_rv ~man man.local exp in + man.sideg (V.thread tid) (G.create_thread rv); (* TODO: emit thread return event so other analyses are aware? *) (* TODO: publish still needed? *) - publish_all ctx `Return; (* like normal return *) - let ask = Analyses.ask_of_ctx ctx in + publish_all man `Return; (* like normal return *) + let ask = Analyses.ask_of_man man in match ThreadId.get_current ask with | `Lifted tid when ThreadReturn.is_current ask -> - ignore @@ Priv.thread_return ask (priv_getg ctx.global) (priv_sideg ctx.sideg) tid st + ignore @@ Priv.thread_return ask (priv_getg man.global) (priv_sideg man.sideg) tid st | _ -> ()) | _ -> () end; @@ -2565,47 +2565,47 @@ struct | MutexAttrSetType {attr = attr; typ = mtyp}, _ -> begin let get_type lval = - let address = eval_lv ~ctx st lval in + let address = eval_lv ~man st lval in AD.type_of address in let dst_lval = mkMem ~addr:(Cil.stripCasts attr) ~off:NoOffset in let dest_typ = get_type dst_lval in - let dest_a = eval_lv ~ctx st dst_lval in - match eval_rv ~ctx st mtyp with + let dest_a = eval_lv ~man st dst_lval in + match eval_rv ~man st mtyp with | Int x -> begin match ID.to_int x with | Some z -> if M.tracing then M.tracel "attr" "setting"; - set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.of_int z)) - | None -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) + set ~man st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.of_int z)) + | None -> set ~man st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) end - | _ -> set ~ctx st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) + | _ -> set ~man st dest_a dest_typ (MutexAttr (ValueDomain.MutexAttr.top ())) end | Identity e, _ -> begin match lv with - | Some x -> assign ctx x e - | None -> ctx.local + | Some x -> assign man x e + | None -> man.local end (**Floating point classification and trigonometric functions defined in c99*) | Math { fun_args; }, _ -> let apply_unary fk float_fun x = - let eval_x = eval_rv ~ctx st x in + let eval_x = eval_rv ~man st x in begin match eval_x with | Float float_x -> float_fun (FD.cast_to fk float_x) | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end in let apply_binary fk float_fun x y = - let eval_x = eval_rv ~ctx st x in - let eval_y = eval_rv ~ctx st y in + let eval_x = eval_rv ~man st x in + let eval_y = eval_rv ~man st y in begin match eval_x, eval_y with | Float float_x, Float float_y -> float_fun (FD.cast_to fk float_x) (FD.cast_to fk float_y) | _ -> failwith ("non-floating-point argument in call to function "^f.vname) end in let apply_abs ik x = - let eval_x = eval_rv ~ctx st x in + let eval_x = eval_rv ~man st x in begin match eval_x with | Int int_x -> let xcast = ID.cast_to ik int_x in @@ -2655,38 +2655,38 @@ struct end in begin match lv with - | Some lv_val -> set ~ctx st (eval_lv ~ctx st lv_val) (Cilfacade.typeOfLval lv_val) result + | Some lv_val -> set ~man st (eval_lv ~man st lv_val) (Cilfacade.typeOfLval lv_val) result | None -> st end (* handling thread creations *) | ThreadCreate _, _ -> - invalidate_ret_lv ctx.local (* actual results joined via threadspawn *) + invalidate_ret_lv man.local (* actual results joined via threadspawn *) (* handling thread joins... sort of *) | ThreadJoin { thread = id; ret_var }, _ -> let st' = (* TODO: should invalidate shallowly? https://github.com/goblint/analyzer/pull/1224#discussion_r1405826773 *) - match eval_rv ~ctx st ret_var with + match eval_rv ~man st ret_var with | Int n when GobOption.exists (Z.equal Z.zero) (ID.to_int n) -> st | Address ret_a -> - begin match eval_rv ~ctx st id with - | Thread a when ValueDomain.Threads.is_top a -> invalidate ~must:true ~ctx st [ret_var] + begin match eval_rv ~man st id with + | Thread a when ValueDomain.Threads.is_top a -> invalidate ~must:true ~man st [ret_var] | Thread a -> - let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (ctx.global (V.thread x))) (ValueDomain.Threads.elements a)) in + let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (man.global (V.thread x))) (ValueDomain.Threads.elements a)) in (* TODO: is this type right? *) - set ~ctx st ret_a (Cilfacade.typeOf ret_var) v - | _ -> invalidate ~must:true ~ctx st [ret_var] + set ~man st ret_a (Cilfacade.typeOf ret_var) v + | _ -> invalidate ~must:true ~man st [ret_var] end - | _ -> invalidate ~must:true ~ctx st [ret_var] + | _ -> invalidate ~must:true ~man st [ret_var] in let st' = invalidate_ret_lv st' in - Priv.thread_join (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st' + Priv.thread_join (Analyses.ask_of_man man) (priv_getg man.global) id st' | Unknown, "__goblint_assume_join" -> let id = List.hd args in - Priv.thread_join ~force:true (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st + Priv.thread_join ~force:true (Analyses.ask_of_man man) (priv_getg man.global) id st | ThreadSelf, _ -> - begin match lv, ThreadId.get_current (Analyses.ask_of_ctx ctx) with + begin match lv, ThreadId.get_current (Analyses.ask_of_man man) with | Some lv, `Lifted tid -> - set ~ctx st (eval_lv ~ctx st lv) (Cilfacade.typeOfLval lv) (Thread (ValueDomain.Threads.singleton tid)) + set ~man st (eval_lv ~man st lv) (Cilfacade.typeOfLval lv) (Thread (ValueDomain.Threads.singleton tid)) | Some lv, _ -> invalidate_ret_lv st | None, _ -> @@ -2695,10 +2695,10 @@ struct | Alloca size, _ -> begin match lv with | Some lv -> - let heap_var = AD.of_var (heap_var true ctx) in - (* ignore @@ printf "alloca will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) - set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, ZeroInit.malloc)); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] + let heap_var = AD.of_var (heap_var true man) in + (* ignore @@ printf "alloca will allocate %a bytes\n" ID.pretty (eval_int ~man size); *) + set_many ~man st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~man st size, ZeroInit.malloc)); + (eval_lv ~man st lv, (Cilfacade.typeOfLval lv), Address heap_var)] | _ -> st end | Malloc size, _ -> begin @@ -2706,47 +2706,47 @@ struct | Some lv -> let heap_var = if (get_bool "sem.malloc.fail") - then AD.join (AD.of_var (heap_var false ctx)) AD.null_ptr - else AD.of_var (heap_var false ctx) + then AD.join (AD.of_var (heap_var false man)) AD.null_ptr + else AD.of_var (heap_var false man) in - (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ~ctx size); *) - set_many ~ctx st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~ctx st size, ZeroInit.malloc)); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address heap_var)] + (* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ~man size); *) + set_many ~man st [(heap_var, TVoid [], Blob (VD.bot (), eval_int ~man st size, ZeroInit.malloc)); + (eval_lv ~man st lv, (Cilfacade.typeOfLval lv), Address heap_var)] | _ -> st end | Calloc { count = n; size }, _ -> begin match lv with | Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *) - let heap_var = heap_var false ctx in + let heap_var = heap_var false man in let add_null addr = if get_bool "sem.malloc.fail" then AD.join addr AD.null_ptr (* calloc can fail and return NULL *) else addr in let ik = Cilfacade.ptrdiff_ikind () in - let sizeval = eval_int ~ctx st size in - let countval = eval_int ~ctx st n in + let sizeval = eval_int ~man st size in + let countval = eval_int ~man st n in if ID.to_int countval = Some Z.one then ( - set_many ~ctx st [ + set_many ~man st [ (add_null (AD.of_var heap_var), TVoid [], Blob (VD.bot (), sizeval, ZeroInit.calloc)); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var))) + (eval_lv ~man st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_var heap_var))) ] ) else ( let blobsize = ID.mul (ID.cast_to ik @@ sizeval) (ID.cast_to ik @@ countval) in (* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *) - set_many ~ctx st [ + set_many ~man st [ (add_null (AD.of_var heap_var), TVoid [], Array (CArrays.make (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.one) (Blob (VD.bot (), blobsize, ZeroInit.calloc)))); - (eval_lv ~ctx st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero, `NoOffset))))) + (eval_lv ~man st lv, (Cilfacade.typeOfLval lv), Address (add_null (AD.of_mval (heap_var, `Index (IdxDom.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero, `NoOffset))))) ] ) | _ -> st end | Realloc { ptr = p; size }, _ -> (* Realloc shouldn't be passed non-dynamically allocated memory *) - check_invalid_mem_dealloc ctx f p; + check_invalid_mem_dealloc man f p; begin match lv with | Some lv -> - let p_rv = eval_rv ~ctx st p in + let p_rv = eval_rv ~man st p in let p_addr = match p_rv with | Address a -> a @@ -2756,18 +2756,18 @@ struct | _ -> AD.top_ptr (* TODO: why does this ever happen? *) in let p_addr' = AD.remove NullPtr p_addr in (* realloc with NULL is same as malloc, remove to avoid unknown value from NullPtr access *) - let p_addr_get = get ~ctx st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) - let size_int = eval_int ~ctx st size in + let p_addr_get = get ~man st p_addr' None in (* implicitly includes join of malloc value (VD.bot) *) + let size_int = eval_int ~man st size in let heap_val:value = Blob (p_addr_get, size_int, ZeroInit.malloc) in (* copy old contents with new size *) - let heap_addr = AD.of_var (heap_var false ctx) in + let heap_addr = AD.of_var (heap_var false man) in let heap_addr' = if get_bool "sem.malloc.fail" then AD.join heap_addr AD.null_ptr else heap_addr in - let lv_addr = eval_lv ~ctx st lv in - set_many ~ctx st [ + let lv_addr = eval_lv ~man st lv in + set_many ~man st [ (heap_addr, TVoid [], heap_val); (lv_addr, Cilfacade.typeOfLval lv, Address heap_addr'); ] (* TODO: free (i.e. invalidate) old blob if successful? *) @@ -2776,21 +2776,21 @@ struct end | Free ptr, _ -> (* Free shouldn't be passed non-dynamically allocated memory *) - check_invalid_mem_dealloc ctx f ptr; + check_invalid_mem_dealloc man f ptr; st - | Assert { exp; refine; _ }, _ -> assert_fn ctx exp refine + | Assert { exp; refine; _ }, _ -> assert_fn man exp refine | Setjmp { env }, _ -> - let st' = match eval_rv ~ctx st env with + let st' = match eval_rv ~man st env with | Address jmp_buf -> - let value = VD.JmpBuf (ValueDomain.JmpBufs.Bufs.singleton (Target (ctx.prev_node, ctx.control_context ())), false) in - let r = set ~ctx st jmp_buf (Cilfacade.typeOf env) value in + let value = VD.JmpBuf (ValueDomain.JmpBufs.Bufs.singleton (Target (man.prev_node, man.control_context ())), false) in + let r = set ~man st jmp_buf (Cilfacade.typeOf env) value in if M.tracing then M.tracel "setjmp" "setting setjmp %a on %a -> %a" d_exp env D.pretty st D.pretty r; r | _ -> failwith "problem?!" in begin match lv with | Some lv -> - set ~ctx st' (eval_lv ~ctx st lv) (Cilfacade.typeOfLval lv) (Int (ID.of_int IInt Z.zero)) + set ~man st' (eval_lv ~man st lv) (Cilfacade.typeOfLval lv) (Int (ID.of_int IInt Z.zero)) | None -> st' end | Longjmp {env; value}, _ -> @@ -2810,19 +2810,19 @@ struct M.warn ~category:Program "Arguments to longjmp are strange!"; rv in - let rv = ensure_not_zero @@ eval_rv ~ctx ctx.local value in + let rv = ensure_not_zero @@ eval_rv ~man man.local value in let t = Cilfacade.typeOf value in - set ~ctx ~t_override:t ctx.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *) + set ~man ~t_override:t man.local (AD.of_var !longjmp_return) t rv (* Not raising Deadcode here, deadcode is raised at a higher level! *) | Rand, _ -> begin match lv with | Some x -> let result:value = (Int (ID.starting IInt Z.zero)) in - set ~ctx st (eval_lv ~ctx st x) (Cilfacade.typeOfLval x) result + set ~man st (eval_lv ~man st x) (Cilfacade.typeOfLval x) result | None -> st end | _, _ -> let st = - special_unknown_invalidate ctx f args + special_unknown_invalidate man f args (* * TODO: invalidate vars reachable via args * publish globals @@ -2835,7 +2835,7 @@ struct in if get_bool "sem.noreturn.dead_code" && Cil.hasAttribute "noreturn" f.vattr then raise Deadcode else st - let combine_st ctx (local_st : store) (fun_st : store) (tainted_lvs : AD.t) : store = + let combine_st man (local_st : store) (fun_st : store) (tainted_lvs : AD.t) : store = AD.fold (fun addr (st: store) -> match addr with | Addr.Addr (v,o) when CPA.mem v fun_st.cpa -> @@ -2851,9 +2851,9 @@ struct | _ -> begin let address = AD.singleton addr in - let new_val = get ~ctx fun_st address None in + let new_val = get ~man fun_st address None in if M.tracing then M.trace "taintPC" "update val: %a" VD.pretty new_val; - let st' = set_savetop ~ctx st address lval_type new_val in + let st' = set_savetop ~man st address lval_type new_val in match Dep.find_opt v fun_st.deps with | None -> st' (* if a var partitions an array, all cpa-info for arrays it may partition are added from callee to caller *) @@ -2866,7 +2866,7 @@ struct | _ -> st ) tainted_lvs local_st - let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = + let combine_env man lval fexp f args fc au (f_ask: Queries.ask) = let combine_one (st: D.t) (fun_st: D.t) = if M.tracing then M.tracel "combine" "%a\n%a" CPA.pretty st.cpa CPA.pretty fun_st.cpa; (* This function does miscellaneous things, but the main task was to give the @@ -2877,7 +2877,7 @@ struct let add_globals (st: store) (fun_st: store) = (* Remove the return value as this is dealt with separately. *) let cpa_noreturn = CPA.remove (return_varinfo ()) fun_st.cpa in - let ask = Analyses.ask_of_ctx ctx in + let ask = Analyses.ask_of_man man in let tainted = f_ask.f Q.MayBeTainted in if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a" f.svar.vname AD.pretty tainted; if M.tracing then M.trace "taintPC" "combine base:\ncaller: %a\ncallee: %a" CPA.pretty st.cpa CPA.pretty fun_st.cpa; @@ -2900,7 +2900,7 @@ struct | Addr.Addr (v,_) -> not (CPA.mem v cpa_new) | _ -> false ) tainted in - let st_combined = combine_st ctx {st with cpa = cpa_caller'} fun_st tainted in + let st_combined = combine_st man {st with cpa = cpa_caller'} fun_st tainted in if M.tracing then M.trace "taintPC" "combined: %a" CPA.pretty st_combined.cpa; { fun_st with cpa = st_combined.cpa } in @@ -2912,20 +2912,20 @@ struct | Some n -> Node.find_fundec n | None -> failwith "callerfundec not found" in - let cpa' = project (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) (Some p) nst.cpa callerFundec in + let cpa' = project (Queries.to_value_domain_ask (Analyses.ask_of_man man)) (Some p) nst.cpa callerFundec in if get_bool "sem.noreturn.dead_code" && Cil.hasAttribute "noreturn" f.svar.vattr then raise Deadcode; { nst with cpa = cpa'; weak = st.weak } (* keep weak from caller *) in - combine_one ctx.local au + combine_one man.local au - let combine_assign ctx (lval: lval option) fexp (f: fundec) (args: exp list) fc (after: D.t) (f_ask: Q.ask) : D.t = + let combine_assign man (lval: lval option) fexp (f: fundec) (args: exp list) fc (after: D.t) (f_ask: Q.ask) : D.t = let combine_one (st: D.t) (fun_st: D.t) = let return_var = return_var () in let return_val = if CPA.mem (return_varinfo ()) fun_st.cpa - then get ~ctx fun_st return_var None + then get ~man fun_st return_var None else VD.top () in @@ -2935,46 +2935,46 @@ struct | Some n -> Node.find_fundec n | None -> failwith "callerfundec not found" in - let return_val = project_val (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) (attributes_varinfo (return_varinfo ()) callerFundec) (Some p) return_val (is_privglob (return_varinfo ())) in + let return_val = project_val (Queries.to_value_domain_ask (Analyses.ask_of_man man)) (attributes_varinfo (return_varinfo ()) callerFundec) (Some p) return_val (is_privglob (return_varinfo ())) in match lval with | None -> st - | Some lval -> set_savetop ~ctx st (eval_lv ~ctx st lval) (Cilfacade.typeOfLval lval) return_val + | Some lval -> set_savetop ~man st (eval_lv ~man st lval) (Cilfacade.typeOfLval lval) return_val in - combine_one ctx.local after + combine_one man.local after - let threadenter ctx ~multiple (lval: lval option) (f: varinfo) (args: exp list): D.t list = + let threadenter man ~multiple (lval: lval option) (f: varinfo) (args: exp list): D.t list = match Cilfacade.find_varinfo_fundec f with | fd -> - [make_entry ~thread:true ctx fd args] + [make_entry ~thread:true man fd args] | exception Not_found -> (* Unknown functions *) - let st = special_unknown_invalidate ctx f args in + let st = special_unknown_invalidate man f args in [st] - let threadspawn ctx ~multiple (lval: lval option) (f: varinfo) (args: exp list) fctx: D.t = + let threadspawn man ~multiple (lval: lval option) (f: varinfo) (args: exp list) fman: D.t = begin match lval with | Some lval -> - begin match ThreadId.get_current (Analyses.ask_of_ctx fctx) with + begin match ThreadId.get_current (Analyses.ask_of_man fman) with | `Lifted tid -> - (* Cannot set here, because ctx isn't in multithreaded mode and set wouldn't side-effect if lval is global. *) - ctx.emit (Events.AssignSpawnedThread (lval, tid)) + (* Cannot set here, because man isn't in multithreaded mode and set wouldn't side-effect if lval is global. *) + man.emit (Events.AssignSpawnedThread (lval, tid)) | _ -> () end | None -> () end; - (* D.join ctx.local @@ *) - Priv.threadspawn (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) (priv_sideg ctx.sideg) ctx.local + (* D.join man.local @@ *) + Priv.threadspawn (Analyses.ask_of_man man) (priv_getg man.global) (priv_sideg man.sideg) man.local - let unassume (ctx: (D.t, _, _, _) ctx) e uuids = + let unassume (man: (D.t, _, _, _) man) e uuids = (* 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. *) + let man_with_local ~single local = + (* The usual recursion trick for man. *) + (* Must change man used by ask to also use new st (not man.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' ~querycache asked = - { ctx with + let rec man' ~querycache asked = + { man with ask = (fun (type a) (q: a Queries.t) -> query' ~querycache asked q) ; local } @@ -3010,57 +3010,57 @@ struct 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 + man.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 + query (man' ~querycache asked') q in Queries.Hashtbl.replace querycache anyq (Obj.repr r); r ) in let querycache = Queries.Hashtbl.create 13 in - ctx' ~querycache Queries.Set.empty + man' ~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 *) - let local: D.t = {ctx.local with cpa = CPA.bot ()} in - let octx = ctx_with_local ~single:false (D.join ctx.local st) in (* original ctx with non-top values *) + let local: D.t = {man.local with cpa = CPA.bot ()} in + let oman = man_with_local ~single:false (D.join man.local st) in (* original man with non-top values *) (* TODO: deduplicate with invariant *) - let ctx = ctx_with_local ~single:true local in + let man = man_with_local ~single:true local in let module UnassumeEval = struct module D = D module V = V module G = G - let ost = octx.local + let ost = oman.local let unop_ID = unop_ID let unop_FD = unop_FD - (* all evals happen in octx with non-top values *) - let eval_rv ~ctx st e = eval_rv ~ctx:octx ost e - let eval_rv_address ~ctx st e = eval_rv_address ~ctx:octx ost e - let eval_lv ~ctx st lv = eval_lv ~ctx:octx ost lv - let convert_offset ~ctx st o = convert_offset ~ctx:octx ost o + (* all evals happen in oman with non-top values *) + let eval_rv ~man st e = eval_rv ~man:oman ost e + let eval_rv_address ~man st e = eval_rv_address ~man:oman ost e + let eval_lv ~man st lv = eval_lv ~man:oman ost lv + let convert_offset ~man st o = convert_offset ~man:oman ost o - (* all updates happen in ctx with top values *) + (* all updates happen in man with top values *) let get_var = get_var - let get ~ctx st addrs exp = get ~ctx st addrs exp - let set ~ctx st lval lval_type ?lval_raw value = set ~ctx ~invariant:false st lval lval_type ?lval_raw value (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) + let get ~man st addrs exp = get ~man st addrs exp + let set ~man st lval lval_type ?lval_raw value = set ~man ~invariant:false st lval lval_type ?lval_raw value (* TODO: should have invariant false? doesn't work with empty cpa then, because meets *) let refine_entire_var = false let map_oldval oldval t_lval = if VD.is_bot oldval then VD.top_value t_lval else oldval - let eval_rv_lval_refine ~ctx st exp lv = - (* new, use different ctx for eval_lv (for Mem): *) - eval_rv_base_lval ~eval_lv ~ctx st exp lv + let eval_rv_lval_refine ~man st exp lv = + (* new, use different man for eval_lv (for Mem): *) + eval_rv_base_lval ~eval_lv ~man st exp lv - (* don't meet with current octx values when propagating inverse operands down *) + (* don't meet with current oman values when propagating inverse operands down *) let id_meet_down ~old ~c = c let fd_meet_down ~old ~c = c @@ -3069,7 +3069,7 @@ struct in let module Unassume = BaseInvariant.Make (UnassumeEval) in try - Unassume.invariant ctx ctx.local e true + Unassume.invariant man man.local e true with Deadcode -> (* contradiction in unassume *) D.bot () in @@ -3095,47 +3095,47 @@ struct WideningTokenLifter.with_side_tokens (WideningTokenLifter.TS.of_list uuids) (fun () -> CPA.fold (fun x v acc -> let addr: AD.t = AD.of_mval (x, `NoOffset) in - set ~ctx ~invariant:false acc addr x.vtype v - ) e_d.cpa ctx.local + set ~man ~invariant:false acc addr x.vtype v + ) e_d.cpa man.local ) in - D.join ctx.local e_d' + D.join man.local e_d' - let event ctx e octx = - let ask = Analyses.ask_of_ctx ctx in - let st: store = ctx.local in + let event man e oman = + let ask = Analyses.ask_of_man man in + let st: store = man.local in match e with | Events.Lock (addr, _) when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) if M.tracing then M.tracel "priv" "LOCK EVENT %a" LockDomain.Addr.pretty addr; CommonPriv.lift_lock ask (fun st m -> - Priv.lock ask (priv_getg ctx.global) st m + Priv.lock ask (priv_getg man.global) st m ) st addr | Events.Unlock addr when ThreadFlag.has_ever_been_multi ask -> (* TODO: is this condition sound? *) WideningTokenLifter.with_local_side_tokens (fun () -> CommonPriv.lift_unlock ask (fun st m -> - Priv.unlock ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st m + Priv.unlock ask (priv_getg man.global) (priv_sideg man.sideg) st m ) st addr ) | Events.Escape escaped -> - Priv.escape ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st escaped + Priv.escape ask (priv_getg man.global) (priv_sideg man.sideg) st escaped | Events.EnterMultiThreaded -> - Priv.enter_multithreaded ask (priv_getg ctx.global) (priv_sideg ctx.sideg) st + Priv.enter_multithreaded ask (priv_getg man.global) (priv_sideg man.sideg) st | Events.AssignSpawnedThread (lval, tid) -> (* TODO: is this type right? *) - set ~ctx ctx.local (eval_lv ~ctx ctx.local lval) (Cilfacade.typeOfLval lval) (Thread (ValueDomain.Threads.singleton tid)) + set ~man man.local (eval_lv ~man man.local lval) (Cilfacade.typeOfLval lval) (Thread (ValueDomain.Threads.singleton tid)) | Events.Assert exp -> - assert_fn ctx exp true + assert_fn man exp true | Events.Unassume {exp; tokens} -> - Timing.wrap "base unassume" (unassume ctx exp) tokens + Timing.wrap "base unassume" (unassume man exp) tokens | Events.Longjmped {lval} -> begin match lval with | Some lval -> - let st' = assign ctx lval (Lval (Cil.var !longjmp_return)) in + let st' = assign man lval (Lval (Cil.var !longjmp_return)) in {st' with cpa = CPA.remove !longjmp_return st'.cpa} - | None -> ctx.local + | None -> man.local end | _ -> - ctx.local + man.local end module type MainSpec = sig diff --git a/src/analyses/baseInvariant.ml b/src/analyses/baseInvariant.ml index 52f0888d3f..19a9999ecf 100644 --- a/src/analyses/baseInvariant.ml +++ b/src/analyses/baseInvariant.ml @@ -17,18 +17,18 @@ sig val unop_ID: Cil.unop -> ID.t -> ID.t val unop_FD: Cil.unop -> FD.t -> VD.t - val eval_rv: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> exp -> VD.t - val eval_rv_address: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> exp -> VD.t - val eval_lv: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> lval -> AD.t - val convert_offset: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> offset -> ID.t Offset.t + val eval_rv: man:(D.t, G.t, _, V.t) Analyses.man -> D.t -> exp -> VD.t + val eval_rv_address: man:(D.t, G.t, _, V.t) Analyses.man -> D.t -> exp -> VD.t + val eval_lv: man:(D.t, G.t, _, V.t) Analyses.man -> D.t -> lval -> AD.t + val convert_offset: man:(D.t, G.t, _, V.t) Analyses.man -> D.t -> offset -> ID.t Offset.t - val get_var: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> varinfo -> VD.t - val get: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> AD.t -> exp option -> VD.t - val set: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> AD.t -> typ -> ?lval_raw:lval -> VD.t -> D.t + val get_var: man:(D.t, G.t, _, V.t) Analyses.man -> D.t -> varinfo -> VD.t + val get: man:(D.t, G.t, _, V.t) Analyses.man -> D.t -> AD.t -> exp option -> VD.t + val set: man:(D.t, G.t, _, V.t) Analyses.man -> D.t -> AD.t -> typ -> ?lval_raw:lval -> VD.t -> D.t val refine_entire_var: bool val map_oldval: VD.t -> typ -> VD.t - val eval_rv_lval_refine: ctx:(D.t, G.t, _, V.t) Analyses.ctx -> D.t -> exp -> lval -> VD.t + val eval_rv_lval_refine: man:(D.t, G.t, _, V.t) Analyses.man -> 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 @@ -54,12 +54,12 @@ struct VD.meet old_val new_val with Lattice.Uncomparable -> old_val - let refine_lv_fallback ctx st lval value tv = + let refine_lv_fallback man st lval value tv = if M.tracing then M.tracec "invariant" "Restricting %a with %a" d_lval lval VD.pretty value; - let addr = eval_lv ~ctx st lval in + let addr = eval_lv ~man st lval in if (AD.is_top addr) then st else - let old_val = get ~ctx 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 old_val = get ~man 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 old_val = map_oldval old_val t_lval in let old_val = @@ -70,8 +70,8 @@ struct else old_val in - let state_with_excluded = set st addr t_lval value ~ctx in - let value = get ~ctx state_with_excluded addr None in + let state_with_excluded = set st addr t_lval value ~man in + let value = get ~man state_with_excluded addr None in let new_val = apply_invariant ~old_val ~new_val:value in if M.tracing then M.traceu "invariant" "New value is %a" VD.pretty new_val; (* make that address meet the invariant, i.e exclusion sets will be joined *) @@ -80,18 +80,18 @@ struct contra st ) else if VD.is_bot new_val - then set st addr t_lval value ~ctx (* no *_raw because this is not a real assignment *) - else set st addr t_lval new_val ~ctx (* no *_raw because this is not a real assignment *) + then set st addr t_lval value ~man (* no *_raw because this is not a real assignment *) + else set st addr t_lval new_val ~man (* no *_raw because this is not a real assignment *) - let refine_lv ctx st c x c' pretty exp = - let set' lval v st = set st (eval_lv ~ctx st lval) (Cilfacade.typeOfLval lval) ~lval_raw:lval v ~ctx in + let refine_lv man st c x c' pretty exp = + let set' lval v st = set st (eval_lv ~man st lval) (Cilfacade.typeOfLval lval) ~lval_raw:lval v ~man in match x with | Var var, o when refine_entire_var -> (* For variables, this is done at to the level of entire variables to benefit e.g. from disjunctive struct domains *) - let old_val = get_var ~ctx st var in + let old_val = get_var ~man st var in let old_val = map_oldval old_val var.vtype in - let offs = convert_offset ~ctx st o in - let new_val = VD.update_offset (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) old_val offs c' (Some exp) x (var.vtype) in + let offs = convert_offset ~man st o in + let new_val = VD.update_offset (Queries.to_value_domain_ask (Analyses.ask_of_man man)) old_val offs c' (Some exp) x (var.vtype) in let v = apply_invariant ~old_val ~new_val in if is_some_bot v then contra st else ( @@ -103,7 +103,7 @@ struct | Var _, _ | Mem _, _ -> (* For accesses via pointers, not yet *) - let old_val = eval_rv_lval_refine ~ctx st exp x in + let old_val = eval_rv_lval_refine ~man st exp x in let old_val = map_oldval old_val (Cilfacade.typeOfLval x) in let v = apply_invariant ~old_val ~new_val:c' in if is_some_bot v then contra st @@ -112,7 +112,7 @@ struct set' x v st ) - let invariant_fallback ctx st exp tv = + let invariant_fallback man 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): [> `Refine of lval * VD.t | `NotUnderstood] = @@ -139,7 +139,7 @@ struct end | Address n -> begin if M.tracing then M.tracec "invariant" "Yes, %a is not %a" d_lval x AD.pretty n; - match eval_rv_address ~ctx st (Lval x) with + match eval_rv_address ~man st (Lval x) with | Address a when AD.is_definite n -> `Refine (x, Address (AD.diff a n)) | Top when AD.is_null n -> @@ -203,12 +203,12 @@ struct 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 ~ctx st rval)) tv + | BinOp(op, Lval x, rval, typ) -> helper op x (VD.cast (Cilfacade.typeOfLval x) (eval_rv ~man 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_statically_safe_cast t1 (Cilfacade.typeOf c1) && VD.is_statically_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) -> - begin match eval_rv ~ctx st (Lval x) with + begin match eval_rv ~man st (Lval x) with | Int v -> if VD.is_dynamically_safe_cast t1 (Cilfacade.typeOfLval x) (Int v) then derived_invariant (BinOp (op, Lval x, rval, typ)) tv @@ -233,7 +233,7 @@ struct in match derived_invariant exp tv with | `Refine (lval, value) -> - refine_lv_fallback ctx st lval value tv + refine_lv_fallback man st lval value tv | `NothingToRefine -> if M.tracing then M.traceu "invariant" "Nothing to refine."; st @@ -242,10 +242,10 @@ struct M.debug ~category:Analyzer "Invariant failed: expression \"%a\" not understood." d_exp exp; st - let invariant ctx st exp tv: D.t = + let invariant man st exp tv: D.t = let fallback reason st = if M.tracing then M.tracel "inv" "Can't handle %a.\n%t" d_plainexp exp reason; - invariant_fallback ctx st exp tv + invariant_fallback man 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). *) @@ -549,7 +549,7 @@ struct a, b with FloatDomain.ArithmeticOnFloatBot _ -> raise Analyses.Deadcode in - let eval e st = eval_rv ~ctx st e in + let eval e st = eval_rv ~man st e in let eval_bool e st = match eval e st with Int i -> ID.to_bool i | _ -> None in let unroll_fk_of_exp e = match unrollType (Cilfacade.typeOf e) with @@ -703,7 +703,7 @@ struct | Float c -> invert_binary_op c FD.pretty (fun ik -> FD.to_int ik c) (fun fk -> FD.cast_to fk c) | _ -> failwith "unreachable") | Lval x, (Int _ | Float _ | Address _) -> (* meet x with c *) - let update_lval c x c' pretty = refine_lv ctx st c x c' pretty exp in + let update_lval c x c' pretty = refine_lv man st c x c' pretty exp in let t = Cil.unrollType (Cilfacade.typeOfLval x) in (* unroll type to deal with TNamed *) if M.tracing then M.trace "invSpecial" "invariant with Lval %a, c_typed %a, type %a" d_lval x VD.pretty c_typed d_type t; begin match c_typed with @@ -718,7 +718,7 @@ struct (* handle special calls *) begin match x, t with | (Var v, offs), TInt (ik, _) -> - let tmpSpecial = ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in + let tmpSpecial = man.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in if M.tracing then M.trace "invSpecial" "qry Result: %a" Queries.ML.pretty tmpSpecial; begin match tmpSpecial with | `Lifted (Abs (ik, xInt)) -> @@ -756,7 +756,7 @@ struct (* handle special calls *) begin match x, t with | (Var v, offs), TFloat (fk, _) -> - let tmpSpecial = ctx.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in + let tmpSpecial = man.ask (Queries.TmpSpecial (v, Offset.Exp.of_cil offs)) in if M.tracing then M.trace "invSpecial" "qry Result: %a" Queries.ML.pretty tmpSpecial; begin match tmpSpecial with | `Lifted (Ceil (ret_fk, xFloat)) -> inv_exp (Float (FD.inv_ceil (FD.cast_to ret_fk c))) xFloat st @@ -835,9 +835,9 @@ struct in inv_exp (Float ftv) exp st - let invariant ctx st exp tv = + let invariant man st exp tv = (* The computations that happen here are not computations that happen in the programs *) (* Any overflow during the forward evaluation will already have been flagged here *) GobRef.wrap AnalysisState.executing_speculative_computations true - @@ fun () -> invariant ctx st exp tv + @@ fun () -> invariant man st exp tv end diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 3afd758daa..a8696a1532 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -57,7 +57,7 @@ end let old_threadenter (type d) ask (st: d BaseDomain.basecomponents_t) = (* Copy-paste from Base make_entry *) let globals = CPA.filter (fun k v -> is_global ask k) st.cpa in - (* let new_cpa = if !earlyglobs || ThreadFlag.is_multi ctx.ask then CPA.filter (fun k v -> is_private ctx.ask ctx.local k) globals else globals in *) + (* let new_cpa = if !earlyglobs || ThreadFlag.is_multi man.ask then CPA.filter (fun k v -> is_private man.ask man.local k) globals else globals in *) let new_cpa = globals in {st with cpa = new_cpa} @@ -1802,7 +1802,7 @@ struct let write_global ?invariant ask getg sideg st x v = time "write_global" (Priv.write_global ?invariant ask getg sideg st x) v let lock ask getg cpa m = time "lock" (Priv.lock ask getg cpa) m let unlock ask getg sideg st m = time "unlock" (Priv.unlock ask getg sideg st) m - let sync reason ctx = time "sync" (Priv.sync reason) ctx + let sync reason man = time "sync" (Priv.sync reason) man let escape ask getg sideg st escaped = time "escape" (Priv.escape ask getg sideg st) escaped let enter_multithreaded ask getg sideg st = time "enter_multithreaded" (Priv.enter_multithreaded ask getg sideg) st let threadenter ask st = time "threadenter" (Priv.threadenter ask) st diff --git a/src/analyses/callstring.ml b/src/analyses/callstring.ml index 391f5f6657..66c2773c59 100644 --- a/src/analyses/callstring.ml +++ b/src/analyses/callstring.ml @@ -13,7 +13,7 @@ module type CallstringType = sig include CilType.S val ana_name: string - val new_ele: fundec -> ('d,'g,'c,'v) ctx -> t option (* returns an element that should be pushed to the call string *) + val new_ele: fundec -> ('d,'g,'c,'v) man -> t option (* returns an element that should be pushed to the call string *) end (** Analysis with infinite call string or with limited call string (k-CFA, tracks the last k call stack elements). @@ -49,17 +49,17 @@ struct let startcontext () = CallString.empty - let context ctx fd _ = - let elem = CT.new_ele fd ctx in (* receive element that should be added to call string *) - CallString.push (ctx.context ()) elem + let context man fd _ = + let elem = CT.new_ele fd man in (* receive element that should be added to call string *) + CallString.push (man.context ()) elem end (* implementations of CallstringTypes*) module Callstring: CallstringType = struct include CilType.Fundec let ana_name = "string" - let new_ele f ctx = - let f' = Node.find_fundec ctx.node in + let new_ele f man = + let f' = Node.find_fundec man.node in if CilType.Fundec.equal f' dummyFunDec then None else Some f' @@ -68,8 +68,8 @@ end module Callsite: CallstringType = struct include CilType.Stmt let ana_name = "site" - let new_ele f ctx = - match ctx.prev_node with + let new_ele f man = + match man.prev_node with | Statement stmt -> Some stmt | _ -> None (* first statement is filtered *) end diff --git a/src/analyses/condVars.ml b/src/analyses/condVars.ml index 448e3a79e5..fbfb2199f2 100644 --- a/src/analyses/condVars.ml +++ b/src/analyses/condVars.ml @@ -63,8 +63,8 @@ struct (* >? is >>=, |? is >> *) let (>?) = Option.bind - let mayPointTo ctx exp = - let ad = ctx.ask (Queries.MayPointTo exp) in + let mayPointTo man exp = + let ad = man.ask (Queries.MayPointTo exp) in let a' = if Queries.AD.mem UnknownPtr ad then ( M.info ~category:Unsound "mayPointTo: query result for %a contains TOP!" d_exp exp; (* UNSOUND *) Queries.AD.remove UnknownPtr ad @@ -75,16 +75,16 @@ struct | _ -> None ) (Queries.AD.elements a') - let mustPointTo ctx exp = (* this is just to get Mval.Exp *) - match mayPointTo ctx exp with + let mustPointTo man exp = (* this is just to get Mval.Exp *) + match mayPointTo man exp with | [clval] -> Some clval | _ -> None (* queries *) - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | Queries.CondVars e -> - let d = ctx.local in + let d = man.local in let rec of_expr tv = function | UnOp (LNot, e, t) when isIntegralType t -> of_expr (not tv) e | BinOp (Ne, e1, e2, t) when isIntegralType t -> of_expr (not tv) (BinOp (Eq, e1, e2, t)) @@ -93,7 +93,7 @@ struct | Lval lval -> Some (tv, lval) | _ -> None in - let of_lval (tv,lval) = Option.map (fun k -> tv, k) @@ mustPointTo ctx (AddrOf lval) in + let of_lval (tv,lval) = Option.map (fun k -> tv, k) @@ mustPointTo man (AddrOf lval) in let t tv e = if tv then e else UnOp (LNot, e, intType) in (* TODO: remove option? *) let f tv v = D.V.map (t tv) v |> fun v -> Some v in @@ -102,11 +102,11 @@ struct | _ -> Queries.Result.top q (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = + let assign man (lval:lval) (rval:exp) : D.t = (* remove all keys lval may point to, and all exprs that contain the variables (TODO precision) *) - let d = List.fold_left (fun d (v,o as k) -> D.remove k d |> D.remove_var v) ctx.local (mayPointTo ctx (AddrOf lval)) in + let d = List.fold_left (fun d (v,o as k) -> D.remove k d |> D.remove_var v) man.local (mayPointTo man (AddrOf lval)) in let save_expr lval expr = - match mustPointTo ctx (AddrOf lval) with + match mustPointTo man (AddrOf lval) with | Some clval -> if M.tracing then M.tracel "condvars" "CondVars: saving %a = %a" Mval.Exp.pretty clval d_exp expr; D.add clval (D.V.singleton expr) d (* if lval must point to clval, add expr *) @@ -117,11 +117,11 @@ struct | BinOp (op, _, _, _) when is_cmp op -> (* logical expression *) save_expr lval rval | Lval k -> (* var-eq for transitive closure *) - mustPointTo ctx (AddrOf k) >? flip D.get_elt d |> Option.map (save_expr lval) |? d + mustPointTo man (AddrOf k) >? flip D.get_elt d |> Option.map (save_expr lval) |? d | _ -> d - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local + let branch man (exp:exp) (tv:bool) : D.t = + man.local (* possible solutions for functions: * 1. only intra-procedural <- we do this @@ -130,33 +130,33 @@ struct * 4. same, but also consider escaped vars *) - let body ctx (f:fundec) : D.t = - ctx.local + let body man (f:fundec) : D.t = + man.local - let return ctx (exp:exp option) (f:fundec) : D.t = - (* D.only_globals ctx.local *) - ctx.local + let return man (exp:exp option) (f:fundec) : D.t = + (* D.only_globals man.local *) + man.local - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - [ctx.local, D.bot ()] + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + [man.local, D.bot ()] - let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = + let combine_env man lval fexp f args fc au (f_ask: Queries.ask) = (* combine caller's state with globals from callee *) (* TODO (precision): globals with only global vars are kept, the rest is lost -> collect which globals are assigned to *) - (* D.merge (fun k s1 s2 -> match s2 with Some ss2 when (fst k).vglob && D.only_global_exprs ss2 -> s2 | _ when (fst k).vglob -> None | _ -> s1) ctx.local au *) + (* D.merge (fun k s1 s2 -> match s2 with Some ss2 when (fst k).vglob && D.only_global_exprs ss2 -> s2 | _ when (fst k).vglob -> None | _ -> s1) man.local au *) let tainted = TaintPartialContexts.conv_varset (f_ask.f Queries.MayBeTainted) in - D.only_untainted ctx.local tainted (* tainted globals might have changed... *) + D.only_untainted man.local tainted (* tainted globals might have changed... *) - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = - ctx.local + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = + man.local - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = (* TODO: shouldn't there be some kind of invalidadte, depending on the effect of the special function? *) - ctx.local + man.local let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadenter man ~multiple lval f args = [D.bot ()] + let threadspawn man ~multiple lval f args fman = man.local let exitstate v = D.bot () end diff --git a/src/analyses/deadlock.ml b/src/analyses/deadlock.ml index e83b122fd9..333b015ad6 100644 --- a/src/analyses/deadlock.ml +++ b/src/analyses/deadlock.ml @@ -23,23 +23,23 @@ struct module G = MapDomain.MapBot (Lock) (MayLockEventPairs) - let side_lock_event_pair ctx ((before_node, _, _) as before) ((after_node, _, _) as after) = + let side_lock_event_pair man ((before_node, _, _) as before) ((after_node, _, _) as after) = if !AnalysisState.should_warn then - ctx.sideg before_node (G.singleton after_node (MayLockEventPairs.singleton (before, after))) + man.sideg before_node (G.singleton after_node (MayLockEventPairs.singleton (before, after))) - let part_access ctx: MCPAccess.A.t = - Obj.obj (ctx.ask (PartAccess Point)) + let part_access man: MCPAccess.A.t = + Obj.obj (man.ask (PartAccess Point)) - let add ctx ((l, _): LockDomain.AddrRW.t) = - let after: LockEvent.t = (l, ctx.prev_node, part_access ctx) in (* use octx for access to use locksets before event *) + let add man ((l, _): LockDomain.AddrRW.t) = + let after: LockEvent.t = (l, man.prev_node, part_access man) in (* use octx for access to use locksets before event *) D.iter (fun before -> - side_lock_event_pair ctx before after - ) ctx.local; - D.add after ctx.local + side_lock_event_pair man before after + ) man.local; + D.add after man.local - let remove ctx l = + let remove man l = let inLockAddrs (e, _, _) = Lock.equal l e in - D.filter (neg inLockAddrs) ctx.local + D.filter (neg inLockAddrs) man.local end include LocksetAnalysis.MakeMay (Arg) @@ -47,7 +47,7 @@ struct module G = Arg.G (* help type checker using explicit constraint *) - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal g -> let g: V.t = Obj.obj g in @@ -105,7 +105,7 @@ struct let new_path_visited_lock_event_pairs' = lock_event_pair :: path_visited_lock_event_pairs in iter_lock new_path_visited_locks new_path_visited_lock_event_pairs' to_lock ) lock_event_pairs - ) (ctx.global lock) + ) (man.global lock) end in diff --git a/src/analyses/expRelation.ml b/src/analyses/expRelation.ml index 39df650bc0..09a644f0f2 100644 --- a/src/analyses/expRelation.ml +++ b/src/analyses/expRelation.ml @@ -47,7 +47,7 @@ struct let isFloat e = Cilfacade.isFloatType (Cilfacade.typeOf e) - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = let lvalsEq l1 l2 = CilType.Lval.equal l1 l2 in (* == would be wrong here *) match q with | Queries.EvalInt (BinOp (Eq, e1, e2, t)) when not (isFloat e1) && Basetype.CilExp.equal (canonize e1) (canonize e2) -> diff --git a/src/analyses/expsplit.ml b/src/analyses/expsplit.ml index 46ec4774e5..6fb2b547a9 100644 --- a/src/analyses/expsplit.ml +++ b/src/analyses/expsplit.ml @@ -21,83 +21,83 @@ struct include Analyses.DefaultSpec module P = IdentityP (D) - let emit_splits ctx d = + let emit_splits man d = D.iter (fun e _ -> - ctx.emit (UpdateExpSplit e) + man.emit (UpdateExpSplit e) ) d; d - let emit_splits_ctx ctx = - emit_splits ctx ctx.local + let emit_splits_ctx man = + emit_splits man man.local - let assign ctx (lval:lval) (rval:exp) = - emit_splits_ctx ctx + let assign man (lval:lval) (rval:exp) = + emit_splits_ctx man - let vdecl ctx (var:varinfo) = - emit_splits_ctx ctx + let vdecl man (var:varinfo) = + emit_splits_ctx man - let branch ctx (exp:exp) (tv:bool) = - emit_splits_ctx ctx + let branch man (exp:exp) (tv:bool) = + emit_splits_ctx man - let enter ctx (lval: lval option) (f:fundec) (args:exp list) = - [ctx.local, ctx.local] + let enter man (lval: lval option) (f:fundec) (args:exp list) = + [man.local, man.local] - let body ctx (f:fundec) = - emit_splits_ctx ctx + let body man (f:fundec) = + emit_splits_ctx man - let return ctx (exp:exp option) (f:fundec) = - emit_splits_ctx ctx + let return man (exp:exp option) (f:fundec) = + emit_splits_ctx man - let combine_env ctx lval fexp f args fc au f_ask = - let d = D.join ctx.local au in - emit_splits ctx d (* Update/preserve splits for globals in combined environment. *) + let combine_env man lval fexp f args fc au f_ask = + let d = D.join man.local au in + emit_splits man d (* Update/preserve splits for globals in combined environment. *) - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc au (f_ask: Queries.ask) = - emit_splits_ctx ctx (* Update/preserve splits over assigned variable. *) + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc au (f_ask: Queries.ask) = + emit_splits_ctx man (* Update/preserve splits over assigned variable. *) - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) = let d = match (LibraryFunctions.find f).special arglist, f.vname with | _, "__goblint_split_begin" -> let exp = List.hd arglist in let ik = Cilfacade.get_ikind_exp exp in (* TODO: something different for pointers, currently casts pointers to ints and loses precision (other than NULL) *) - D.add exp (ID.top_of ik) ctx.local (* split immediately follows *) + D.add exp (ID.top_of ik) man.local (* split immediately follows *) | _, "__goblint_split_end" -> let exp = List.hd arglist in - D.remove exp ctx.local + D.remove exp man.local | Setjmp { env }, _ -> Option.map_default (fun lval -> match GobConfig.get_string "ana.setjmp.split" with - | "none" -> ctx.local + | "none" -> man.local | "precise" -> let e = Lval lval in let ik = Cilfacade.get_ikind_exp e in - D.add e (ID.top_of ik) ctx.local + D.add e (ID.top_of ik) man.local | "coarse" -> let e = Lval lval in let e = BinOp (Eq, e, integer 0, intType) in - D.add e (ID.top_of IInt) ctx.local + D.add e (ID.top_of IInt) man.local | _ -> failwith "Invalid value for ana.setjmp.split" - ) ctx.local lval + ) man.local lval | _ -> - ctx.local + man.local in - emit_splits ctx d + emit_splits man d - let threadenter ctx ~multiple lval f args = [ctx.local] + let threadenter man ~multiple lval f args = [man.local] - let threadspawn ctx ~multiple lval f args fctx = - emit_splits_ctx ctx + let threadspawn man ~multiple lval f args fman = + emit_splits_ctx man - let event ctx (event: Events.t) octx = + let event man (event: Events.t) octx = match event with | UpdateExpSplit exp -> - let value = ctx.ask (EvalInt exp) in - D.add exp value ctx.local + let value = man.ask (EvalInt exp) in + D.add exp value man.local | Longjmped _ -> - emit_splits_ctx ctx + emit_splits_ctx man | _ -> - ctx.local + man.local end let () = diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index a61c54ab96..cd3fce184d 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -233,22 +233,22 @@ let promela_main : fun_name = "mainfun" (* assign tid: promela_main -> 0 *) let _ = Tbls.ThreadTidTbl.get promela_main -let fun_ctx ctx f = - let ctx_hash = - match PthreadDomain.Ctx.to_int ctx with +let fun_ctx man f = + let man_hash = + match PthreadDomain.Ctx.to_int man with | Some i -> i |> i64_to_int |> Tbls.CtxTbl.get |> string_of_int | None -> "TOP" in - f.vname ^ "_" ^ ctx_hash + f.vname ^ "_" ^ man_hash module Tasks = SetDomain.Make (Lattice.Prod (Queries.AD) (PthreadDomain.D)) module rec Env : sig type t - val get : (PthreadDomain.D.t, Tasks.t, PthreadDomain.D.t, _) ctx -> t + val get : (PthreadDomain.D.t, Tasks.t, PthreadDomain.D.t, _) man -> t val d : t -> PthreadDomain.D.t @@ -262,8 +262,8 @@ end = struct ; resource : Resource.t } - let get ctx = - let d : PthreadDomain.D.t = ctx.local in + let get man = + let d : PthreadDomain.D.t = man.local in let node = Option.get !MyCFG.current_node in let fundec = Node.find_fundec node in let thread_name = @@ -452,12 +452,12 @@ module Variables = struct (* all vars on rhs should be already registered, otherwise -> do not add this var *) - let rec all_vars_are_valid ctx = function + let rec all_vars_are_valid man = function | Const _ -> true | Lval l -> let open PthreadDomain in - let d = Env.d @@ Env.get ctx in + let d = Env.d @@ Env.get man in let tid = Int64.to_int @@ Option.get @@ Tid.to_int d.tid in l @@ -465,9 +465,9 @@ module Variables = struct |> Option.map @@ valid_var tid |> Option.default false | UnOp (_, e, _) -> - all_vars_are_valid ctx e + all_vars_are_valid man e | BinOp (_, a, b, _) -> - all_vars_are_valid ctx a && all_vars_are_valid ctx b + all_vars_are_valid man a && all_vars_are_valid man b | _ -> false end @@ -876,56 +876,56 @@ module Spec : Analyses.MCPSpec = struct module ExprEval = struct - let eval_ptr ctx exp = - ctx.ask (Queries.MayPointTo exp) + let eval_ptr man exp = + man.ask (Queries.MayPointTo exp) |> Queries.AD.remove UnknownPtr (* UNSOUND *) |> Queries.AD.to_var_may - let eval_var ctx exp = + let eval_var man exp = match exp with | Lval (Mem e, _) -> - eval_ptr ctx e + eval_ptr man e | Lval (Var v, _) -> [ v ] | _ -> - eval_ptr ctx exp + eval_ptr man exp - let eval_ptr_id ctx exp get = - List.map (get % Variable.show) @@ eval_ptr ctx exp + let eval_ptr_id man exp get = + List.map (get % Variable.show) @@ eval_ptr man exp - let eval_var_id ctx exp get = - List.map (get % Variable.show) @@ eval_var ctx exp + let eval_var_id man exp get = + List.map (get % Variable.show) @@ eval_var man exp end let name () = "extract-pthread" - let assign ctx (lval : lval) (rval : exp) : D.t = + let assign man (lval : lval) (rval : exp) : D.t = let should_ignore_assigns = GobConfig.get_bool "ana.extract-pthread.ignore_assign" in - if PthreadDomain.D.is_bot ctx.local || should_ignore_assigns - then ctx.local + if PthreadDomain.D.is_bot man.local || should_ignore_assigns + then man.local else if Option.is_none !MyCFG.current_node then ( (* it is global var assignment *) let var_opt = Variable.make_from_lval lval in - if Variables.all_vars_are_valid ctx rval + if Variables.all_vars_are_valid man rval (* TODO: handle the assignment of the global *) then Option.may (Variables.add (-1)) var_opt else Option.may (Variables.add_top (-1)) var_opt ; - ctx.local ) + man.local ) else - let env = Env.get ctx in + let env = Env.get man in let d = Env.d env in let tid = Int64.to_int @@ Option.get @@ Tid.to_int d.tid in let var_opt = Variable.make_from_lval lval in - if Option.is_none var_opt || (not @@ Variables.all_vars_are_valid ctx rval) + if Option.is_none var_opt || (not @@ Variables.all_vars_are_valid man rval) then ( (* set lhs var to TOP *) Option.may (Variables.add_top tid) var_opt ; - ctx.local ) + man.local ) else let var = Option.get var_opt in @@ -938,11 +938,11 @@ module Spec : Analyses.MCPSpec = struct { d with pred = Pred.of_node @@ Env.node env } - let branch ctx (exp : exp) (tv : bool) : D.t = - if PthreadDomain.D.is_bot ctx.local - then ctx.local + let branch man (exp : exp) (tv : bool) : D.t = + if PthreadDomain.D.is_bot man.local + then man.local else - let env = Env.get ctx in + let env = Env.get man in let d = Env.d env in let tid = Int64.to_int @@ Option.get @@ Tid.to_int d.tid in let is_valid_var = @@ -981,7 +981,7 @@ module Spec : Analyses.MCPSpec = struct Tbls.NodeTbl.get (if tv then then_stmt else else_stmt).sid in Edges.add ~dst:intermediate_node env (Action.Cond pred_str) ; - { ctx.local with pred = Pred.of_node intermediate_node } + { man.local with pred = Pred.of_node intermediate_node } | _ -> failwith "branch: current_node is not an If" in @@ -996,7 +996,7 @@ module Spec : Analyses.MCPSpec = struct when is_valid_var lhostA && is_valid_var lhostB -> add_action @@ pred_str op (var_str lhostA) (var_str lhostB) | _ -> - ctx.local + man.local in let handle_unop x tv = match x with @@ -1004,7 +1004,7 @@ module Spec : Analyses.MCPSpec = struct let pred = (if tv then "" else "!") ^ var_str lhost in add_action pred | _ -> - ctx.local + man.local in match exp with | BinOp (op, a, b, _) -> @@ -1014,26 +1014,26 @@ module Spec : Analyses.MCPSpec = struct | Const (CInt _) -> handle_unop exp tv | _ -> - ctx.local + man.local - let body ctx (f : fundec) : D.t = + let body man (f : fundec) : D.t = (* enter is not called for spawned threads -> initialize them here *) - let context_hash = Int64.of_int (if not !AnalysisState.global_initialization then ControlSpecC.hash (ctx.control_context ()) else 37) in - { ctx.local with ctx = Ctx.of_int context_hash } + let context_hash = Int64.of_int (if not !AnalysisState.global_initialization then ControlSpecC.hash (man.control_context ()) else 37) in + { man.local with ctx = Ctx.of_int context_hash } - let return ctx (exp : exp option) (f : fundec) : D.t = ctx.local + let return man (exp : exp option) (f : fundec) : D.t = man.local - let enter ctx (lval : lval option) (f : fundec) (args : exp list) : + let enter man (lval : lval option) (f : fundec) (args : exp list) : (D.t * D.t) list = (* on function calls (also for main); not called for spawned threads *) - let d_caller = ctx.local in + let d_caller = man.local in let d_callee = - if D.is_bot ctx.local - then ctx.local + if D.is_bot man.local + then man.local else - { ctx.local with + { man.local with pred = Pred.of_node (MyCFG.Function f) ; ctx = Ctx.top () } @@ -1041,14 +1041,14 @@ module Spec : Analyses.MCPSpec = struct (* set predecessor set to start node of function *) [ (d_caller, d_callee) ] - let combine_env ctx lval fexp f args fc au f_ask = - ctx.local + let combine_env man lval fexp f args fc au f_ask = + man.local - let combine_assign ctx (lval : lval option) fexp (f : fundec) (args : exp list) fc (au : D.t) (f_ask: Queries.ask) : D.t = - if D.any_is_bot ctx.local || D.any_is_bot au - then ctx.local + let combine_assign man (lval : lval option) fexp (f : fundec) (args : exp list) fc (au : D.t) (f_ask: Queries.ask) : D.t = + if D.any_is_bot man.local || D.any_is_bot au + then man.local else - let d_caller = ctx.local in + let d_caller = man.local in let d_callee = au in (* check if the callee has some relevant edges, i.e. advanced from the entry point * if not, we generate no edge for the call and keep the predecessors from the caller *) @@ -1059,7 +1059,7 @@ module Spec : Analyses.MCPSpec = struct (* set current node as new predecessor, since something interesting happend during the call *) { d_callee with pred = d_caller.pred; ctx = d_caller.ctx } else - let env = Env.get ctx in + let env = Env.get man in (* write out edges with call to f coming from all predecessor nodes of the caller *) ( if Ctx.to_int d_callee.ctx <> None then @@ -1073,11 +1073,11 @@ module Spec : Analyses.MCPSpec = struct } - let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) : D.t = - if D.any_is_bot ctx.local then - ctx.local + let special man (lval : lval option) (f : varinfo) (arglist : exp list) : D.t = + if D.any_is_bot man.local then + man.local else - let env = Env.get ctx in + let env = Env.get man in let d = Env.d env in let tid = Int64.to_int @@ Option.get @@ Tid.to_int d.tid in @@ -1110,7 +1110,7 @@ module Spec : Analyses.MCPSpec = struct match (LibraryFunctions.find f).special arglist', f.vname, arglist with | ThreadCreate { thread; start_routine = func; _ }, _, _ -> let funs_ad = - let ad = ctx.ask (Queries.ReachableFrom func) in + let ad = man.ask (Queries.ReachableFrom func) in Queries.AD.filter (function | Queries.AD.Addr.Addr mval -> @@ -1128,13 +1128,13 @@ module Spec : Analyses.MCPSpec = struct let tasks = let f_d:PthreadDomain.D.t = { tid = Tid.of_int @@ Int64.of_int tid - ; pred = Pred.of_node (ctx.prev_node) + ; pred = Pred.of_node (man.prev_node) ; ctx = Ctx.top () } in Tasks.singleton (funs_ad, f_d) in - ctx.sideg tasks_var tasks ; + man.sideg tasks_var tasks ; in let thread_create tid = let fun_name = Variable.show thread_fun in @@ -1180,20 +1180,20 @@ module Spec : Analyses.MCPSpec = struct add_actions @@ List.map thread_create - @@ ExprEval.eval_ptr_id ctx thread Tbls.ThreadTidTbl.get + @@ ExprEval.eval_ptr_id man thread Tbls.ThreadTidTbl.get | ThreadJoin { thread; ret_var = thread_ret }, _, _ -> add_actions @@ List.map (fun tid -> Action.ThreadJoin tid) - @@ ExprEval.eval_var_id ctx thread Tbls.ThreadTidTbl.get + @@ ExprEval.eval_var_id man thread Tbls.ThreadTidTbl.get | Lock { lock = mutex; _ }, _, _ -> add_actions @@ List.map (fun mid -> Action.MutexLock mid) - @@ ExprEval.eval_ptr_id ctx mutex Tbls.MutexMidTbl.get + @@ ExprEval.eval_ptr_id man mutex Tbls.MutexMidTbl.get | Unlock mutex, _, _ -> add_actions @@ List.map (fun mid -> Action.MutexUnlock mid) - @@ ExprEval.eval_ptr_id ctx mutex Tbls.MutexMidTbl.get + @@ ExprEval.eval_ptr_id man mutex Tbls.MutexMidTbl.get | ThreadExit _, _ , _ -> add_action Action.ThreadExit | Abort, _, _ -> @@ -1202,22 +1202,22 @@ module Spec : Analyses.MCPSpec = struct (* TODO: reentrant mutex handling *) add_actions @@ List.map (fun mid -> Action.MutexInit mid) - @@ ExprEval.eval_ptr_id ctx mutex Tbls.MutexMidTbl.get + @@ ExprEval.eval_ptr_id man mutex Tbls.MutexMidTbl.get | Unknown, "pthread_cond_init", [ cond_var; cond_var_attr ] -> add_actions @@ List.map (fun id -> Action.CondVarInit id) - @@ ExprEval.eval_ptr_id ctx cond_var Tbls.CondVarIdTbl.get + @@ ExprEval.eval_ptr_id man cond_var Tbls.CondVarIdTbl.get | Broadcast cond_var, _, _ -> add_actions @@ List.map (fun id -> Action.CondVarBroadcast id) - @@ ExprEval.eval_ptr_id ctx cond_var Tbls.CondVarIdTbl.get + @@ ExprEval.eval_ptr_id man cond_var Tbls.CondVarIdTbl.get | Signal cond_var, _, _ -> add_actions @@ List.map (fun id -> Action.CondVarSignal id) - @@ ExprEval.eval_ptr_id ctx cond_var Tbls.CondVarIdTbl.get + @@ ExprEval.eval_ptr_id man cond_var Tbls.CondVarIdTbl.get | Wait {cond = cond_var; mutex = mutex}, _, _ -> - let cond_vars = ExprEval.eval_ptr ctx cond_var in - let mutex_vars = ExprEval.eval_ptr ctx mutex in + let cond_vars = ExprEval.eval_ptr man cond_var in + let mutex_vars = ExprEval.eval_ptr man mutex in let cond_var_action (v, m) = let open Action in CondVarWait @@ -1228,7 +1228,7 @@ module Spec : Analyses.MCPSpec = struct add_actions @@ List.map cond_var_action @@ List.cartesian_product cond_vars mutex_vars - | _ -> ctx.local + | _ -> man.local let startstate v = let open D in @@ -1238,9 +1238,9 @@ module Spec : Analyses.MCPSpec = struct (Ctx.top ()) - let threadenter ctx ~multiple lval f args = - let d : D.t = ctx.local in - let tasks = ctx.global tasks_var in + let threadenter man ~multiple lval f args = + let d : D.t = man.local in + let tasks = man.global tasks_var in (* TODO: optimize finding *) let tasks_f = let var_in_ad ad f = Queries.AD.exists (function @@ -1254,7 +1254,7 @@ module Spec : Analyses.MCPSpec = struct [ { f_d with pred = d.pred } ] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadspawn man ~multiple lval f args fman = man.local let exitstate v = D.top () diff --git a/src/analyses/locksetAnalysis.ml b/src/analyses/locksetAnalysis.ml index c73bd4703e..e781307868 100644 --- a/src/analyses/locksetAnalysis.ml +++ b/src/analyses/locksetAnalysis.ml @@ -18,7 +18,7 @@ struct include Analyses.ValueContexts(D) let startstate v = D.empty () - let threadenter ctx ~multiple lval f args = [D.empty ()] + let threadenter man ~multiple lval f args = [D.empty ()] let exitstate v = D.empty () end @@ -29,8 +29,8 @@ sig module G: Lattice.S module V: SpecSysVar - val add: (D.t, G.t, D.t, V.t) ctx -> LockDomain.AddrRW.t -> D.t - val remove: (D.t, G.t, D.t, V.t) ctx -> ValueDomain.Addr.t -> D.t + val add: (D.t, G.t, D.t, V.t) man -> LockDomain.AddrRW.t -> D.t + val remove: (D.t, G.t, D.t, V.t) man -> ValueDomain.Addr.t -> D.t end module MakeMay (Arg: MayArg) = @@ -41,25 +41,25 @@ struct module G = Arg.G module V = Arg.V - let event ctx e octx = + let event man e oman = match e with | Events.Lock l -> - Arg.add ctx l (* add all locks, including blob and unknown *) + Arg.add man l (* add all locks, including blob and unknown *) | Events.Unlock UnknownPtr -> - ctx.local (* don't remove any locks, including unknown itself *) - | Events.Unlock Addr (v, _) when ctx.ask (IsMultiple v) -> - ctx.local (* don't remove non-unique lock *) + man.local (* don't remove any locks, including unknown itself *) + | Events.Unlock Addr (v, _) when man.ask (IsMultiple v) -> + man.local (* don't remove non-unique lock *) | Events.Unlock l -> - Arg.remove ctx l (* remove definite lock or none in parallel if ambiguous *) + Arg.remove man l (* remove definite lock or none in parallel if ambiguous *) | _ -> - ctx.local + man.local end module type MustArg = sig include MayArg - val remove_all: (D.t, _, D.t, _) ctx -> D.t + val remove_all: (D.t, _, D.t, _) man -> D.t end module MakeMust (Arg: MustArg) = @@ -70,18 +70,18 @@ struct module G = Arg.G module V = Arg.V - let event ctx e octx = + let event man e oman = match e with | Events.Lock (UnknownPtr, _) -> - ctx.local (* don't add unknown lock *) - | Events.Lock (Addr (v, _), _) when ctx.ask (IsMultiple v) -> - ctx.local (* don't add non-unique lock *) + man.local (* don't add unknown lock *) + | Events.Lock (Addr (v, _), _) when man.ask (IsMultiple v) -> + man.local (* don't add non-unique lock *) | Events.Lock l -> - Arg.add ctx l (* add definite lock or none in parallel if ambiguous *) + Arg.add man l (* add definite lock or none in parallel if ambiguous *) | Events.Unlock UnknownPtr -> - Arg.remove_all ctx (* remove all locks *) + Arg.remove_all man (* remove all locks *) | Events.Unlock l -> - Arg.remove ctx l (* remove definite lock or all in parallel if ambiguous (blob lock is never added) *) + Arg.remove man l (* remove definite lock or all in parallel if ambiguous (blob lock is never added) *) | _ -> - ctx.local + man.local end diff --git a/src/analyses/loopTermination.ml b/src/analyses/loopTermination.ml index 66a50c17b7..364151a189 100644 --- a/src/analyses/loopTermination.ml +++ b/src/analyses/loopTermination.ml @@ -8,10 +8,10 @@ open TerminationPreprocessing let loop_counters : stmt VarToStmt.t ref = ref VarToStmt.empty (** Checks whether a variable can be bounded. *) -let check_bounded ctx varinfo = +let check_bounded man varinfo = let open IntDomain.IntDomTuple in let exp = Lval (Var varinfo, NoOffset) in - match ctx.ask (EvalInt exp) with + match man.ask (EvalInt exp) with | `Top -> false | `Lifted v -> not (is_top_of (ikind v) v) | `Bot -> failwith "Loop counter variable is Bot." @@ -46,14 +46,14 @@ struct (** Recognizes a call of [__goblint_bounded] to check the EvalInt of the * respective loop counter variable at that position. *) - let special ctx (lval : lval option) (f : varinfo) (arglist : exp list) = + let special man (lval : lval option) (f : varinfo) (arglist : exp list) = if !AnalysisState.postsolving then match f.vname, arglist with "__goblint_bounded", [Lval (Var loop_counter, NoOffset)] -> (try let loop_statement = find_loop ~loop_counter in - let is_bounded = check_bounded ctx loop_counter in - ctx.sideg () (G.add (`Lifted loop_statement) is_bounded (ctx.global ())); + let is_bounded = check_bounded man loop_counter in + man.sideg () (G.add (`Lifted loop_statement) is_bounded (man.global ())); (* In case the loop is not bounded, a warning is created. *) if not (is_bounded) then ( M.warn ~loc:(M.Location.CilLocation (Cilfacade.get_stmtLoc loop_statement)) ~category:Termination "The program might not terminate! (Loop analysis)" @@ -64,21 +64,21 @@ struct | _ -> () else () - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | Queries.MustTermLoop loop_statement -> - let multithreaded = ctx.ask Queries.IsEverMultiThreaded in + let multithreaded = man.ask Queries.IsEverMultiThreaded in (not multithreaded) - && (match G.find_opt (`Lifted loop_statement) (ctx.global ()) with + && (match G.find_opt (`Lifted loop_statement) (man.global ()) with Some b -> b | None -> false) | Queries.MustTermAllLoops -> - let multithreaded = ctx.ask Queries.IsEverMultiThreaded in + let multithreaded = man.ask Queries.IsEverMultiThreaded in if multithreaded then ( M.warn ~category:Termination "The program might not terminate! (Multithreaded)"; false) else - G.for_all (fun _ term_info -> term_info) (ctx.global ()) + G.for_all (fun _ term_info -> term_info) (man.global ()) | _ -> Queries.Result.top q end diff --git a/src/analyses/loopfreeCallstring.ml b/src/analyses/loopfreeCallstring.ml index d9760e58a0..9226b0b142 100644 --- a/src/analyses/loopfreeCallstring.ml +++ b/src/analyses/loopfreeCallstring.ml @@ -43,7 +43,7 @@ struct append fd (FundecSet.empty ()) [] current let startcontext () = [] - let context ctx fd x = append fd (ctx.context ()) + let context man fd x = append fd (man.context ()) end let _ = MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index f2f36b1360..0420931ffd 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -83,15 +83,15 @@ struct check_deps !activated; activated := topo_sort_an !activated; begin - match get_string_list "ana.ctx_sens" with - | [] -> (* use values of "ana.ctx_insens" (blacklist) *) - let cont_inse = map' find_id @@ get_string_list "ana.ctx_insens" in - activated_ctx_sens := List.filter (fun (n, _) -> not (List.mem n cont_inse)) !activated; - | sens -> (* use values of "ana.ctx_sens" (whitelist) *) + match get_string_list "ana.man_sens" with + | [] -> (* use values of "ana.man_insens" (blacklist) *) + let cont_inse = map' find_id @@ get_string_list "ana.man_insens" in + activated_context_sens := List.filter (fun (n, _) -> not (List.mem n cont_inse)) !activated; + | sens -> (* use values of "ana.man_sens" (whitelist) *) let cont_sens = map' find_id @@ sens in - activated_ctx_sens := List.filter (fun (n, _) -> List.mem n cont_sens) !activated; + activated_context_sens := List.filter (fun (n, _) -> List.mem n cont_sens) !activated; end; - act_cont_sens := Set.of_list (List.map (fun (n,p) -> n) !activated_ctx_sens); + act_cont_sens := Set.of_list (List.map (fun (n,p) -> n) !activated_context_sens); activated_path_sens := List.filter (fun (n, _) -> List.mem n !path_sens) !activated; match marshal with | Some marshal -> @@ -147,16 +147,16 @@ struct f ((k,v::a')::a) b in f [] xs - let do_spawns ctx (xs:(varinfo * (lval option * exp list * bool)) list) = + let do_spawns man (xs:(varinfo * (lval option * exp list * bool)) list) = let spawn_one v d = - List.iter (fun (lval, args, multiple) -> ctx.spawn ~multiple lval v args) d + List.iter (fun (lval, args, multiple) -> man.spawn ~multiple lval v args) d in if get_bool "exp.single-threaded" then M.msg_final Error ~category:Unsound "Thread not spawned" else iter (uncurry spawn_one) @@ group_assoc_eq Basetype.Variables.equal xs - let do_sideg ctx (xs:(V.t * (WideningTokenLifter.TS.t * G.t)) list) = + let do_sideg man (xs:(V.t * (WideningTokenLifter.TS.t * G.t)) list) = let side_one v dts = let side_one_ts ts d = (* Do side effects with the tokens that were active at the time. @@ -164,7 +164,7 @@ struct let old_side_tokens = !WideningTokenLifter.side_tokens in WideningTokenLifter.side_tokens := ts; Fun.protect (fun () -> - ctx.sideg v @@ fold_left G.join (G.bot ()) d + man.sideg v @@ fold_left G.join (G.bot ()) d ) ~finally:(fun () -> WideningTokenLifter.side_tokens := old_side_tokens ) @@ -173,54 +173,54 @@ 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) emits = + let rec do_splits man pv (xs:(int * (Obj.t * Events.t list)) list) emits = let split_one n (d,emits') = let nv = assoc_replace (n,d) pv in (* 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 false) [] + man.split (do_emits man (emits @ List.rev emits') nv false) [] in iter (uncurry split_one) xs - and do_emits ctx emits xs dead = - let octx = ctx in - let ctx_with_local ctx local' = - (* let rec ctx' = - { ctx with + and do_emits man emits xs dead = + let oman = man in + let man_with_local man local' = + (* let rec man' = + { man with local = local'; ask = ask - } - and ask q = query ctx' q - in - ctx' *) - {ctx with local = local'} + } + and ask q = query man' q + in + man' *) + {man with local = local'} in - let do_emit ctx = function + let do_emit man = function | Events.SplitBranch (exp, tv) -> - ctx_with_local ctx (branch ctx exp tv) + man_with_local man (branch man exp tv) | Events.Assign {lval; exp} -> - ctx_with_local ctx (assign ctx lval exp) + man_with_local man (assign man lval exp) | e -> let spawns = ref [] in let splits = ref [] in - let sides = ref [] in (* why do we need to collect these instead of calling ctx.sideg directly? *) + let sides = ref [] in (* why do we need to collect these instead of calling man.sideg directly? *) let emits = ref [] in - let ctx'' = outer_ctx "do_emits" ~spawns ~sides ~emits ctx in - let octx'' = outer_ctx "do_emits" ~spawns ~sides ~emits octx in + let man'' = outer_man "do_emits" ~spawns ~sides ~emits man in + let oman'' = outer_man "do_emits" ~spawns ~sides ~emits oman 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 od in - n, Obj.repr @@ S.event ctx' e octx' + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "do_emits" ~splits ~post_all man'' n d in + let oman' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "do_emits" ~splits ~post_all oman'' n od in + n, Obj.repr @@ S.event man' e oman' in - if M.tracing then M.traceli "event" "%a\n before: %a" 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.traceli "event" "%a\n before: %a" Events.pretty e D.pretty man.local; + let d, q = map_deadcode f @@ spec_list2 man.local oman.local in if M.tracing then M.traceu "event" "%a\n after:%a" 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 q in - if q then raise Deadcode else ctx_with_local ctx d + do_sideg man !sides; + do_spawns man !spawns; + do_splits man d !splits !emits; + let d = do_emits man !emits d q in + if q then raise Deadcode else man_with_local man d in if M.tracing then M.traceli "event" "do_emits:"; let emits = @@ -230,40 +230,40 @@ struct emits in (* [emits] is in reverse order. *) - let ctx' = List.fold_left do_emit (ctx_with_local ctx xs) (List.rev emits) in + let man' = List.fold_left do_emit (man_with_local man xs) (List.rev emits) in if M.tracing then M.traceu "event" ""; - ctx'.local + man'.local - and context ctx fd x = - let ctx'' = outer_ctx "context_computation" ctx in + and context man fd x = + let man'' = outer_man "context_computation" man in let x = spec_list x in filter_map (fun (n,(module S:MCPSpec),d) -> if Set.is_empty !act_cont_sens || not (Set.mem n !act_cont_sens) then (*n is insensitive*) None else - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "context_computation" ctx'' n d in - Some (n, Obj.repr @@ S.context ctx' fd (Obj.obj d)) + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "context_computation" man'' n d in + Some (n, Obj.repr @@ S.context man' fd (Obj.obj d)) ) x - and branch (ctx:(D.t, G.t, C.t, V.t) ctx) (e:exp) (tv:bool) = + and branch (man:(D.t, G.t, C.t, V.t) man) (e:exp) (tv:bool) = let spawns = ref [] in let splits = ref [] in - let sides = ref [] in (* why do we need to collect these instead of calling ctx.sideg directly? *) + let sides = ref [] in (* why do we need to collect these instead of calling man.sideg directly? *) let emits = ref [] in - let ctx'' = outer_ctx "branch" ~spawns ~sides ~emits ctx in + let man'' = outer_man "branch" ~spawns ~sides ~emits man in let f post_all (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "branch" ~splits ~post_all ctx'' n d in - n, Obj.repr @@ S.branch ctx' e tv + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "branch" ~splits ~post_all man'' n d in + n, Obj.repr @@ S.branch man' e tv in - let d, q = map_deadcode f @@ spec_list ctx.local in - do_sideg ctx !sides; - do_spawns ctx !spawns; - do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d q in + let d, q = map_deadcode f @@ spec_list man.local in + do_sideg man !sides; + do_spawns man !spawns; + do_splits man d !splits !emits; + let d = do_emits man !emits d q in if q then raise Deadcode else d (* Explicitly polymorphic type required here for recursive GADT call in ask. *) - and query': type a. querycache:Obj.t Queries.Hashtbl.t -> Queries.Set.t -> (D.t, G.t, C.t, V.t) ctx -> a Queries.t -> a Queries.result = fun ~querycache asked ctx q -> + and query': type a. querycache:Obj.t Queries.Hashtbl.t -> Queries.Set.t -> (D.t, G.t, C.t, V.t) man -> a Queries.t -> a Queries.result = fun ~querycache asked man q -> let anyq = Queries.Any q in if M.tracing then M.traceli "query" "query %a" Queries.Any.pretty anyq; let r = match Queries.Hashtbl.find_option querycache anyq with @@ -279,18 +279,18 @@ struct else let asked' = Queries.Set.add anyq asked in let sides = ref [] in - let ctx'' = outer_ctx "query" ~sides ctx in + let man'' = outer_man "query" ~sides man in let f ~q a (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "query" ctx'' n d in + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "query" man'' n d in (* sideg is discouraged in query, because they would bypass sides grouping in other transfer functions. See https://github.com/goblint/analyzer/pull/214. *) - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = - { ctx' with - ask = (fun (type b) (q: b Queries.t) -> query' ~querycache asked' ctx q) + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = + { man' with + ask = (fun (type b) (q: b Queries.t) -> query' ~querycache asked' man q) } in (* meet results so that precision from all analyses is combined *) - let res = S.query ctx' q in + let res = S.query man' q in if M.tracing then M.trace "queryanswers" "analysis %s query %a -> answer %a" (S.name ()) Queries.Any.pretty anyq Result.pretty res; Result.meet a @@ res in @@ -298,40 +298,40 @@ struct | Queries.WarnGlobal g -> (* WarnGlobal is special: it only goes to corresponding analysis and the argument variant is unlifted for it *) let (n, g): V.t = Obj.obj g in - f ~q:(WarnGlobal (Obj.repr g)) (Result.top ()) (n, spec n, assoc n ctx.local) + f ~q:(WarnGlobal (Obj.repr g)) (Result.top ()) (n, spec n, assoc n man.local) | Queries.InvariantGlobal g -> (* InvariantGlobal is special: it only goes to corresponding analysis and the argument variant is unlifted for it *) let (n, g): V.t = Obj.obj g in - f ~q:(InvariantGlobal (Obj.repr g)) (Result.top ()) (n, spec n, assoc n ctx.local) + f ~q:(InvariantGlobal (Obj.repr g)) (Result.top ()) (n, spec n, assoc n man.local) | Queries.YamlEntryGlobal (g, task) -> (* YamlEntryGlobal is special: it only goes to corresponding analysis and the argument variant is unlifted for it *) let (n, g): V.t = Obj.obj g in - f ~q:(YamlEntryGlobal (Obj.repr g, task)) (Result.top ()) (n, spec n, assoc n ctx.local) + f ~q:(YamlEntryGlobal (Obj.repr g, task)) (Result.top ()) (n, spec n, assoc n man.local) | Queries.PartAccess a -> - Obj.repr (access ctx a) + Obj.repr (access man a) | Queries.IterSysVars (vq, fi) -> (* IterSysVars is special: argument function is lifted for each analysis *) iter (fun ((n,(module S:MCPSpec),d) as t) -> let fi' x = fi (Obj.repr (v_of n x)) in let q' = Queries.IterSysVars (vq, fi') in f ~q:q' () t - ) @@ spec_list ctx.local + ) @@ spec_list man.local (* | EvalInt e -> (* TODO: only query others that actually respond to EvalInt *) (* 2x speed difference on SV-COMP nla-digbench-scaling/ps6-ll_valuebound5.c *) - f (Result.top ()) (!base_id, spec !base_id, assoc !base_id ctx.local) *) + f (Result.top ()) (!base_id, spec !base_id, assoc !base_id man.local) *) | Queries.DYojson -> - `Lifted (D.to_yojson ctx.local) + `Lifted (D.to_yojson man.local) | Queries.GasExhausted f -> if (get_int "ana.context.gas_value" >= 0) then (* There is a lifter above this that will answer it, save to ask *) - ctx.ask (Queries.GasExhausted f) + man.ask (Queries.GasExhausted f) else (* Abort to avoid infinite recursion *) false | _ -> - let r = fold_left (f ~q) (Result.top ()) @@ spec_list ctx.local in - do_sideg ctx !sides; + let r = fold_left (f ~q) (Result.top ()) @@ spec_list man.local in + do_sideg man !sides; Queries.Hashtbl.replace querycache anyq (Obj.repr r); r in @@ -341,19 +341,19 @@ struct ); r - and query: type a. (D.t, G.t, C.t, V.t) ctx -> a Queries.t -> a Queries.result = fun ctx q -> + and query: type a. (D.t, G.t, C.t, V.t) man -> a Queries.t -> a Queries.result = fun man q -> let querycache = Queries.Hashtbl.create 13 in - query' ~querycache Queries.Set.empty ctx q + query' ~querycache Queries.Set.empty man q - and access (ctx:(D.t, G.t, C.t, V.t) ctx) a: MCPAccess.A.t = - let ctx'' = outer_ctx "access" ctx in + and access (man:(D.t, G.t, C.t, V.t) man) a: MCPAccess.A.t = + let man'' = outer_man "access" man in let f (n, (module S: MCPSpec), d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "access" ctx'' n d in - (n, Obj.repr (S.access ctx' a)) + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "access" man'' n d in + (n, Obj.repr (S.access man' a)) in - BatList.map f (spec_list ctx.local) (* map without deadcode *) + BatList.map f (spec_list man.local) (* map without deadcode *) - and outer_ctx tfname ?spawns ?sides ?emits ctx = + and outer_man tfname ?spawns ?sides ?emits man = let spawn = match spawns with | Some spawns -> (fun ?(multiple=false) l v a -> spawns := (v,(l,a,multiple)) :: !spawns) | None -> (fun ?(multiple=false) v d -> failwith ("Cannot \"spawn\" in " ^ tfname ^ " context.")) @@ -368,183 +368,183 @@ struct in let querycache = Queries.Hashtbl.create 13 in (* TODO: make rec? *) - { ctx with - ask = (fun (type a) (q: a Queries.t) -> query' ~querycache Queries.Set.empty ctx q) + { man with + ask = (fun (type a) (q: a Queries.t) -> query' ~querycache Queries.Set.empty man q) ; emit ; spawn ; sideg } (* Explicitly polymorphic type required here for recursive call in branch. *) - and inner_ctx: type d g c v. string -> ?splits:(int * (Obj.t * Events.t list)) list ref -> ?post_all:(int * Obj.t) list -> (D.t, G.t, C.t, V.t) ctx -> int -> Obj.t -> (d, g, c, v) ctx = fun tfname ?splits ?(post_all=[]) ctx n d -> + and inner_man: type d g c v. string -> ?splits:(int * (Obj.t * Events.t list)) list ref -> ?post_all:(int * Obj.t) list -> (D.t, G.t, C.t, V.t) man -> int -> Obj.t -> (d, g, c, v) man = fun tfname ?splits ?(post_all=[]) man n d -> let split = match splits with | Some splits -> (fun d es -> splits := (n,(Obj.repr d,es)) :: !splits) | None -> (fun _ _ -> failwith ("Cannot \"split\" in " ^ tfname ^ " context.")) in - { ctx with + { man with local = Obj.obj d - ; context = (fun () -> ctx.context () |> assoc n |> Obj.obj) - ; global = (fun v -> ctx.global (v_of n v) |> g_to n |> Obj.obj) + ; context = (fun () -> man.context () |> assoc n |> Obj.obj) + ; global = (fun v -> man.global (v_of n v) |> g_to n |> Obj.obj) ; split - ; sideg = (fun v g -> ctx.sideg (v_of n v) (g_of n g)) + ; sideg = (fun v g -> man.sideg (v_of n v) (g_of n g)) } - and assign (ctx:(D.t, G.t, C.t, V.t) ctx) l e = + and assign (man:(D.t, G.t, C.t, V.t) man) l e = let spawns = ref [] in let splits = ref [] in let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "assign" ~spawns ~sides ~emits ctx in + let man'' = outer_man "assign" ~spawns ~sides ~emits man in let f post_all (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "assign" ~splits ~post_all ctx'' n d in - n, Obj.repr @@ S.assign ctx' l e + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "assign" ~splits ~post_all man'' n d in + n, Obj.repr @@ S.assign man' l e in - let d, q = map_deadcode f @@ spec_list ctx.local in - do_sideg ctx !sides; - do_spawns ctx !spawns; - do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d q in + let d, q = map_deadcode f @@ spec_list man.local in + do_sideg man !sides; + do_spawns man !spawns; + do_splits man d !splits !emits; + let d = do_emits man !emits d q in if q then raise Deadcode else d - let vdecl (ctx:(D.t, G.t, C.t, V.t) ctx) v = + let vdecl (man:(D.t, G.t, C.t, V.t) man) v = let spawns = ref [] in let splits = ref [] in let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "vdecl" ~spawns ~sides ~emits ctx in + let man'' = outer_man "vdecl" ~spawns ~sides ~emits man in let f post_all (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "vdecl" ~splits ~post_all ctx'' n d in - n, Obj.repr @@ S.vdecl ctx' v + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "vdecl" ~splits ~post_all man'' n d in + n, Obj.repr @@ S.vdecl man' v in - let d, q = map_deadcode f @@ spec_list ctx.local in - do_sideg ctx !sides; - do_spawns ctx !spawns; - do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d q in + let d, q = map_deadcode f @@ spec_list man.local in + do_sideg man !sides; + do_spawns man !spawns; + do_splits man d !splits !emits; + let d = do_emits man !emits d q in if q then raise Deadcode else d - let body (ctx:(D.t, G.t, C.t, V.t) ctx) f = + let body (man:(D.t, G.t, C.t, V.t) man) f = let spawns = ref [] in let splits = ref [] in let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "body" ~spawns ~sides ~emits ctx in + let man'' = outer_man "body" ~spawns ~sides ~emits man in let f post_all (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "body" ~splits ~post_all ctx'' n d in - n, Obj.repr @@ S.body ctx' f + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "body" ~splits ~post_all man'' n d in + n, Obj.repr @@ S.body man' f in - let d, q = map_deadcode f @@ spec_list ctx.local in - do_sideg ctx !sides; - do_spawns ctx !spawns; - do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d q in + let d, q = map_deadcode f @@ spec_list man.local in + do_sideg man !sides; + do_spawns man !spawns; + do_splits man d !splits !emits; + let d = do_emits man !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 = + let return (man:(D.t, G.t, C.t, V.t) man) e f = let spawns = ref [] in let splits = ref [] in let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "return" ~spawns ~sides ~emits ctx in + let man'' = outer_man "return" ~spawns ~sides ~emits man in let f post_all (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "return" ~splits ~post_all ctx'' n d in - n, Obj.repr @@ S.return ctx' e f + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "return" ~splits ~post_all man'' n d in + n, Obj.repr @@ S.return man' e f in - let d, q = map_deadcode f @@ spec_list ctx.local in - do_sideg ctx !sides; - do_spawns ctx !spawns; - do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d q in + let d, q = map_deadcode f @@ spec_list man.local in + do_sideg man !sides; + do_spawns man !spawns; + do_splits man d !splits !emits; + let d = do_emits man !emits d q in if q then raise Deadcode else d - let asm (ctx:(D.t, G.t, C.t, V.t) ctx) = + let asm (man:(D.t, G.t, C.t, V.t) man) = let spawns = ref [] in let splits = ref [] in let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "asm" ~spawns ~sides ~emits ctx in + let man'' = outer_man "asm" ~spawns ~sides ~emits man in let f post_all (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "asm" ~splits ~post_all ctx'' n d in - n, Obj.repr @@ S.asm ctx' + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "asm" ~splits ~post_all man'' n d in + n, Obj.repr @@ S.asm man' in - let d, q = map_deadcode f @@ spec_list ctx.local in - do_sideg ctx !sides; - do_spawns ctx !spawns; - do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d q in + let d, q = map_deadcode f @@ spec_list man.local in + do_sideg man !sides; + do_spawns man !spawns; + do_splits man d !splits !emits; + let d = do_emits man !emits d q in if q then raise Deadcode else d - let skip (ctx:(D.t, G.t, C.t, V.t) ctx) = + let skip (man:(D.t, G.t, C.t, V.t) man) = let spawns = ref [] in let splits = ref [] in let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "skip" ~spawns ~sides ~emits ctx in + let man'' = outer_man "skip" ~spawns ~sides ~emits man in let f post_all (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "skip" ~splits ~post_all ctx'' n d in - n, Obj.repr @@ S.skip ctx' + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "skip" ~splits ~post_all man'' n d in + n, Obj.repr @@ S.skip man' in - let d, q = map_deadcode f @@ spec_list ctx.local in - do_sideg ctx !sides; - do_spawns ctx !spawns; - do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d q in + let d, q = map_deadcode f @@ spec_list man.local in + do_sideg man !sides; + do_spawns man !spawns; + do_splits man d !splits !emits; + let d = do_emits man !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 = + let special (man:(D.t, G.t, C.t, V.t) man) r f a = let spawns = ref [] in let splits = ref [] in let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "special" ~spawns ~sides ~emits ctx in + let man'' = outer_man "special" ~spawns ~sides ~emits man in let f post_all (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "special" ~splits ~post_all ctx'' n d in - n, Obj.repr @@ S.special ctx' r f a + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "special" ~splits ~post_all man'' n d in + n, Obj.repr @@ S.special man' r f a in - let d, q = map_deadcode f @@ spec_list ctx.local in - do_sideg ctx !sides; - do_spawns ctx !spawns; - do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d q in + let d, q = map_deadcode f @@ spec_list man.local in + do_sideg man !sides; + do_spawns man !spawns; + do_splits man d !splits !emits; + let d = do_emits man !emits d q in if q then raise Deadcode else d - let sync (ctx:(D.t, G.t, C.t, V.t) ctx) reason = + let sync (man:(D.t, G.t, C.t, V.t) man) reason = let spawns = ref [] in let splits = ref [] in let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "sync" ~spawns ~sides ~emits ctx in + let man'' = outer_man "sync" ~spawns ~sides ~emits man in let f post_all (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "sync" ~splits ~post_all ctx'' n d in - n, Obj.repr @@ S.sync ctx' reason + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "sync" ~splits ~post_all man'' n d in + n, Obj.repr @@ S.sync man' reason in - let d, q = map_deadcode f @@ spec_list ctx.local in - do_sideg ctx !sides; - do_spawns ctx !spawns; - do_splits ctx d !splits !emits; - let d = do_emits ctx !emits d q in + let d, q = map_deadcode f @@ spec_list man.local in + do_sideg man !sides; + do_spawns man !spawns; + do_splits man d !splits !emits; + let d = do_emits man !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 = + let enter (man:(D.t, G.t, C.t, V.t) man) r f a = let spawns = ref [] in let sides = ref [] in - let ctx'' = outer_ctx "enter" ~spawns ~sides ctx in + let man'' = outer_man "enter" ~spawns ~sides man in let f (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "enter" ctx'' n d in - map (fun (c,d) -> ((n, Obj.repr c), (n, Obj.repr d))) @@ S.enter ctx' r f a + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "enter" man'' n d in + map (fun (c,d) -> ((n, Obj.repr c), (n, Obj.repr d))) @@ S.enter man' r f a in - let css = map f @@ spec_list ctx.local in - do_sideg ctx !sides; - do_spawns ctx !spawns; + let css = map f @@ spec_list man.local in + do_sideg man !sides; + do_spawns man !spawns; map (fun xs -> (topo_sort_an @@ map fst xs, topo_sort_an @@ map snd xs)) @@ n_cartesian_product css - let combine_env (ctx:(D.t, G.t, C.t, V.t) ctx) r fe f a fc fd f_ask = + let combine_env (man:(D.t, G.t, C.t, V.t) man) r fe f a fc fd f_ask = let spawns = ref [] in let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "combine_env" ~spawns ~sides ~emits ctx in + let man'' = outer_man "combine_env" ~spawns ~sides ~emits man in (* Like spec_list2 but for three lists. Tail recursion like map3_rev would have. Due to context-insensitivity, second list is optional and may only contain a subset of analyses in the same order, so some skipping needs to happen to align the three lists. @@ -560,20 +560,20 @@ struct | _, _, _ -> invalid_arg "MCP.spec_list3_rev_acc" in let f post_all (n,(module S:MCPSpec),(d,fc,fd)) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "combine_env" ~post_all ctx'' n d in - n, Obj.repr @@ S.combine_env ctx' r fe f a (Option.map Obj.obj fc) (Obj.obj fd) f_ask + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "combine_env" ~post_all man'' n d in + n, Obj.repr @@ S.combine_env man' r fe f a (Option.map Obj.obj fc) (Obj.obj fd) f_ask in - 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 q in + let d, q = map_deadcode f @@ List.rev @@ spec_list3_rev_acc [] man.local fc fd in + do_sideg man !sides; + do_spawns man !spawns; + let d = do_emits man !emits d q in if q then raise Deadcode else d - let combine_assign (ctx:(D.t, G.t, C.t, V.t) ctx) r fe f a fc fd f_ask = + let combine_assign (man:(D.t, G.t, C.t, V.t) man) r fe f a fc fd f_ask = let spawns = ref [] in let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "combine_assign" ~spawns ~sides ~emits ctx in + let man'' = outer_man "combine_assign" ~spawns ~sides ~emits man in (* Like spec_list2 but for three lists. Tail recursion like map3_rev would have. Due to context-insensitivity, second list is optional and may only contain a subset of analyses in the same order, so some skipping needs to happen to align the three lists. @@ -589,45 +589,45 @@ struct | _, _, _ -> invalid_arg "MCP.spec_list3_rev_acc" in let f post_all (n,(module S:MCPSpec),(d,fc,fd)) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "combine_assign" ~post_all ctx'' n d in - n, Obj.repr @@ S.combine_assign ctx' r fe f a (Option.map Obj.obj fc) (Obj.obj fd) f_ask + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "combine_assign" ~post_all man'' n d in + n, Obj.repr @@ S.combine_assign man' r fe f a (Option.map Obj.obj fc) (Obj.obj fd) f_ask in - 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 q in + let d, q = map_deadcode f @@ List.rev @@ spec_list3_rev_acc [] man.local fc fd in + do_sideg man !sides; + do_spawns man !spawns; + let d = do_emits man !emits d q in if q then raise Deadcode else d - let threadenter (ctx:(D.t, G.t, C.t, V.t) ctx) ~multiple lval f a = + let threadenter (man:(D.t, G.t, C.t, V.t) man) ~multiple lval f a = let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "threadenter" ~sides ~emits ctx in + let man'' = outer_man "threadenter" ~sides ~emits man in let f (n,(module S:MCPSpec),d) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "threadenter" ctx'' n d in - map (fun d -> (n, Obj.repr d)) @@ (S.threadenter ~multiple) ctx' lval f a + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "threadenter" man'' n d in + map (fun d -> (n, Obj.repr d)) @@ (S.threadenter ~multiple) man' lval f a in - let css = map f @@ spec_list ctx.local in - do_sideg ctx !sides; + let css = map f @@ spec_list man.local in + do_sideg man !sides; (* TODO: this do_emits is now different from everything else *) - map (fun d -> do_emits ctx !emits d false) @@ map topo_sort_an @@ n_cartesian_product css + map (fun d -> do_emits man !emits d false) @@ map topo_sort_an @@ n_cartesian_product css - let threadspawn (ctx:(D.t, G.t, C.t, V.t) ctx) ~multiple lval f a fctx = + let threadspawn (man:(D.t, G.t, C.t, V.t) man) ~multiple lval f a fman = let sides = ref [] in let emits = ref [] in - let ctx'' = outer_ctx "threadspawn" ~sides ~emits ctx in - let fctx'' = outer_ctx "threadspawn" ~sides ~emits fctx in + let man'' = outer_man "threadspawn" ~sides ~emits man in + let fman'' = outer_man "threadspawn" ~sides ~emits fman in let f post_all (n,(module S:MCPSpec),(d,fd)) = - let ctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "threadspawn" ~post_all ctx'' n d in - let fctx' : (S.D.t, S.G.t, S.C.t, S.V.t) ctx = inner_ctx "threadspawn" ~post_all fctx'' n fd in - n, Obj.repr @@ S.threadspawn ~multiple ctx' lval f a fctx' + let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "threadspawn" ~post_all man'' n d in + let fman' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "threadspawn" ~post_all fman'' n fd in + n, Obj.repr @@ S.threadspawn ~multiple man' lval f a fman' 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 q in + let d, q = map_deadcode f @@ spec_list2 man.local fman.local in + do_sideg man !sides; + let d = do_emits man !emits d q in if q then raise Deadcode else d - let event (ctx:(D.t, G.t, C.t, V.t) ctx) e _ = do_emits ctx [e] ctx.local false + let event (man:(D.t, G.t, C.t, V.t) man) e _ = do_emits man [e] man.local false (* Just to satisfy signature *) - let paths_as_set ctx = [ctx.local] + let paths_as_set man = [man.local] end diff --git a/src/analyses/mCPRegistry.ml b/src/analyses/mCPRegistry.ml index 43ccd690f2..991af6b6af 100644 --- a/src/analyses/mCPRegistry.ml +++ b/src/analyses/mCPRegistry.ml @@ -17,7 +17,7 @@ type spec_modules = { name : string ; path : (module DisjointDomain.Representative) } let activated : (int * spec_modules) list ref = ref [] -let activated_ctx_sens: (int * spec_modules) list ref = ref [] +let activated_context_sens: (int * spec_modules) list ref = ref [] let activated_path_sens: (int * spec_modules) list ref = ref [] let registered: (int, spec_modules) Hashtbl.t = Hashtbl.create 100 let registered_name: (string, int) Hashtbl.t = Hashtbl.create 100 @@ -437,7 +437,7 @@ end module ContextListSpec : DomainListPrintableSpec = struct let assoc_dom n = (find_spec n).cont - let domain_list () = List.map (fun (n,p) -> n, p.cont) !activated_ctx_sens + let domain_list () = List.map (fun (n,p) -> n, p.cont) !activated_context_sens end module VarListSpec : DomainListSysVarSpec = diff --git a/src/analyses/mHPAnalysis.ml b/src/analyses/mHPAnalysis.ml index e9c3364c9f..a44e5340ac 100644 --- a/src/analyses/mHPAnalysis.ml +++ b/src/analyses/mHPAnalysis.ml @@ -18,7 +18,7 @@ struct not (ConcDomain.ThreadSet.is_empty must_joined)) end - let access ctx _: MHP.t = MHP.current (Analyses.ask_of_ctx ctx) + let access man _: MHP.t = MHP.current (Analyses.ask_of_man man) end let _ = diff --git a/src/analyses/mallocFresh.ml b/src/analyses/mallocFresh.ml index 020046c678..c226d7e6ce 100644 --- a/src/analyses/mallocFresh.ml +++ b/src/analyses/mallocFresh.ml @@ -26,23 +26,23 @@ struct ) ad -> D.empty () | _ -> local - let assign ctx lval rval = - assign_lval (Analyses.ask_of_ctx ctx) lval ctx.local + let assign man lval rval = + assign_lval (Analyses.ask_of_man man) lval man.local - let combine_env ctx lval fexp f args fc au f_ask = - ctx.local (* keep local as opposed to IdentitySpec *) + let combine_env man lval fexp f args fc au f_ask = + man.local (* keep local as opposed to IdentitySpec *) - let combine_assign ctx lval f fd args context f_local (f_ask: Queries.ask) = + let combine_assign man lval f fd args context f_local (f_ask: Queries.ask) = match lval with | None -> f_local - | Some lval -> assign_lval (Analyses.ask_of_ctx ctx) lval f_local + | Some lval -> assign_lval (Analyses.ask_of_man man) lval f_local - let special ctx lval f args = + let special man lval f args = let desc = LibraryFunctions.find f in let alloc_var on_stack = - match ctx.ask (AllocVar {on_stack = on_stack}) with - | `Lifted var -> D.add var ctx.local - | _ -> ctx.local + match man.ask (AllocVar {on_stack = on_stack}) with + | `Lifted var -> D.add var man.local + | _ -> man.local in match desc.special args with | Malloc _ @@ -51,13 +51,13 @@ struct | Alloca _ -> alloc_var true | _ -> match lval with - | None -> ctx.local - | Some lval -> assign_lval (Analyses.ask_of_ctx ctx) lval ctx.local + | None -> man.local + | Some lval -> assign_lval (Analyses.ask_of_man man) lval man.local - let threadenter ctx ~multiple lval f args = + let threadenter man ~multiple lval f args = [D.empty ()] - let threadspawn ctx ~multiple lval f args fctx = + let threadspawn man ~multiple lval f args fman = D.empty () module A = @@ -67,10 +67,10 @@ struct let may_race f1 f2 = not (f1 || f2) let should_print f = f end - let access ctx (a: Queries.access) = + let access man (a: Queries.access) = match a with | Memory {var_opt = Some v; _} -> - D.mem v ctx.local + D.mem v man.local | _ -> false end diff --git a/src/analyses/malloc_null.ml b/src/analyses/malloc_null.ml index 5e6225caac..07f798583e 100644 --- a/src/analyses/malloc_null.ml +++ b/src/analyses/malloc_null.ml @@ -145,78 +145,78 @@ struct *) (* One step tf-s *) - let assign ctx (lval:lval) (rval:exp) : D.t = - warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local (Lval lval) ; - warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local rval; - match get_concrete_exp rval ctx.global ctx.local, get_concrete_lval (Analyses.ask_of_ctx ctx) lval with - | Some rv, Some mval when might_be_null (Analyses.ask_of_ctx ctx) rv ctx.global ctx.local -> - D.add (Addr.of_mval mval) ctx.local - | _ -> ctx.local + let assign man (lval:lval) (rval:exp) : D.t = + warn_deref_exp (Analyses.ask_of_man man) man.local (Lval lval) ; + warn_deref_exp (Analyses.ask_of_man man) man.local rval; + match get_concrete_exp rval man.global man.local, get_concrete_lval (Analyses.ask_of_man man) lval with + | Some rv, Some mval when might_be_null (Analyses.ask_of_man man) rv man.global man.local -> + D.add (Addr.of_mval mval) man.local + | _ -> man.local - let branch ctx (exp:exp) (tv:bool) : D.t = - warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local exp; - ctx.local + let branch man (exp:exp) (tv:bool) : D.t = + warn_deref_exp (Analyses.ask_of_man man) man.local exp; + man.local - let body ctx (f:fundec) : D.t = - ctx.local + let body man (f:fundec) : D.t = + man.local let return_addr_ = ref Addr.NullPtr let return_addr () = !return_addr_ - let return ctx (exp:exp option) (f:fundec) : D.t = + let return man (exp:exp option) (f:fundec) : D.t = let remove_var x v = List.fold_right D.remove (to_addrs v) x in - let nst = List.fold_left remove_var ctx.local (f.slocals @ f.sformals) in + let nst = List.fold_left remove_var man.local (f.slocals @ f.sformals) in match exp with | Some ret -> - warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local ret; - begin match get_concrete_exp ret ctx.global ctx.local with - | Some ev when might_be_null (Analyses.ask_of_ctx ctx) ev ctx.global ctx.local -> + warn_deref_exp (Analyses.ask_of_man man) man.local ret; + begin match get_concrete_exp ret man.global man.local with + | Some ev when might_be_null (Analyses.ask_of_man man) ev man.global man.local -> D.add (return_addr ()) nst | _ -> nst end | None -> nst (* Function calls *) - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - let nst = remove_unreachable (Analyses.ask_of_ctx ctx) args ctx.local in - Option.iter (fun x -> warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local (Lval x)) lval; - List.iter (warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local) args; - [ctx.local,nst] + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let nst = remove_unreachable (Analyses.ask_of_man man) args man.local in + Option.iter (fun x -> warn_deref_exp (Analyses.ask_of_man man) man.local (Lval x)) lval; + List.iter (warn_deref_exp (Analyses.ask_of_man man) man.local) args; + [man.local,nst] - let combine_env ctx lval fexp f args fc au f_ask = - let cal_st = remove_unreachable (Analyses.ask_of_ctx ctx) args ctx.local in - D.union (D.remove (return_addr ()) au) (D.diff ctx.local cal_st) + let combine_env man lval fexp f args fc au f_ask = + let cal_st = remove_unreachable (Analyses.ask_of_man man) args man.local in + D.union (D.remove (return_addr ()) au) (D.diff man.local cal_st) - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = match lval, D.mem (return_addr ()) au with | Some lv, true -> - begin match get_concrete_lval (Analyses.ask_of_ctx ctx) lv with - | Some mval -> D.add (Addr.of_mval mval) ctx.local - | _ -> ctx.local + begin match get_concrete_lval (Analyses.ask_of_man man) lv with + | Some mval -> D.add (Addr.of_mval mval) man.local + | _ -> man.local end - | _ -> ctx.local + | _ -> man.local - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - Option.iter (fun x -> warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local (Lval x)) lval; - List.iter (warn_deref_exp (Analyses.ask_of_ctx ctx) ctx.local) arglist; + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + Option.iter (fun x -> warn_deref_exp (Analyses.ask_of_man man) man.local (Lval x)) lval; + List.iter (warn_deref_exp (Analyses.ask_of_man man) man.local) arglist; let desc = LibraryFunctions.find f in match desc.special arglist, lval with | Malloc _, Some lv -> begin - match get_concrete_lval (Analyses.ask_of_ctx ctx) lv with + match get_concrete_lval (Analyses.ask_of_man man) lv with | Some mval -> - ctx.split ctx.local [Events.SplitBranch ((Lval lv), true)]; - ctx.split (D.add (Addr.of_mval mval) ctx.local) [Events.SplitBranch ((Lval lv), false)]; + man.split man.local [Events.SplitBranch ((Lval lv), true)]; + man.split (D.add (Addr.of_mval mval) man.local) [Events.SplitBranch ((Lval lv), false)]; raise Analyses.Deadcode - | _ -> ctx.local + | _ -> man.local end - | _ -> ctx.local + | _ -> man.local let name () = "malloc_null" let startstate v = D.empty () - let threadenter ctx ~multiple lval f args = [D.empty ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadenter man ~multiple lval f args = [D.empty ()] + let threadspawn man ~multiple lval f args fman = man.local let exitstate v = D.empty () let init marshal = diff --git a/src/analyses/mayLocks.ml b/src/analyses/mayLocks.ml index 853005de87..13806ac59d 100644 --- a/src/analyses/mayLocks.ml +++ b/src/analyses/mayLocks.ml @@ -10,34 +10,34 @@ struct module G = DefaultSpec.G module V = DefaultSpec.V - let add ctx (l,r) = - if D.mem l ctx.local then + let add man (l,r) = + if D.mem l man.local then let default () = M.warn ~category:M.Category.Behavior.Undefined.double_locking "Acquiring a (possibly non-recursive) mutex that may be already held"; - ctx.local + man.local in match D.Addr.to_mval l with | Some (v,o) -> - (let mtype = ctx.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) in + (let mtype = man.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) in match mtype with - | `Lifted MutexAttrDomain.MutexKind.Recursive -> ctx.local + | `Lifted MutexAttrDomain.MutexKind.Recursive -> man.local | `Lifted MutexAttrDomain.MutexKind.NonRec -> M.warn ~category:M.Category.Behavior.Undefined.double_locking "Acquiring a non-recursive mutex that may be already held"; - ctx.local + man.local | _ -> default ()) | _ -> default () else - D.add l ctx.local + D.add l man.local - let remove ctx l = - if not (D.mem l ctx.local) then M.warn "Releasing a mutex that is definitely not held"; + let remove man l = + if not (D.mem l man.local) then M.warn "Releasing a mutex that is definitely not held"; match D.Addr.to_mval l with | Some (v,o) -> - (let mtype = ctx.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) in + (let mtype = man.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) in match mtype with - | `Lifted MutexAttrDomain.MutexKind.NonRec -> D.remove l ctx.local - | _ -> ctx.local (* we cannot remove them here *)) - | None -> ctx.local (* we cannot remove them here *) + | `Lifted MutexAttrDomain.MutexKind.NonRec -> D.remove l man.local + | _ -> man.local (* we cannot remove them here *)) + | None -> man.local (* we cannot remove them here *) end module Spec = @@ -47,16 +47,16 @@ struct let exitstate v = D.top () (* TODO: why? *) - let return ctx exp fundec = - if not (D.is_bot ctx.local) && ThreadReturn.is_current (Analyses.ask_of_ctx ctx) then M.warn "Exiting thread while still holding a mutex!"; - ctx.local + let return man exp fundec = + if not (D.is_bot man.local) && ThreadReturn.is_current (Analyses.ask_of_man man) then M.warn "Exiting thread while still holding a mutex!"; + man.local - let special ctx (lv:lval option) (f: varinfo) (args: exp list) = + let special man (lv:lval option) (f: varinfo) (args: exp list) = (match(LF.find f).special args with - | ThreadExit _ -> if not @@ D.is_bot ctx.local then M.warn "Exiting thread while still holding a mutex!" + | ThreadExit _ -> if not @@ D.is_bot man.local then M.warn "Exiting thread while still holding a mutex!" | _ -> ()) ; - ctx.local + man.local end let _ = diff --git a/src/analyses/memLeak.ml b/src/analyses/memLeak.ml index 362800b7b4..7930b00103 100644 --- a/src/analyses/memLeak.ml +++ b/src/analyses/memLeak.ml @@ -20,13 +20,13 @@ struct module V = UnitV module G = WasMallocCalled - let context ctx _ d = d + let context man _ d = d - let must_be_single_threaded ~since_start ctx = - ctx.ask (Queries.MustBeSingleThreaded { since_start }) + let must_be_single_threaded ~since_start man = + man.ask (Queries.MustBeSingleThreaded { since_start }) - let was_malloc_called ctx = - ctx.global () + let was_malloc_called man = + man.global () (* HELPER FUNCTIONS *) let get_global_vars () = @@ -50,21 +50,21 @@ struct | (TNamed ({ttype = TComp (ci,_); _}, _)) -> ci.cstruct | _ -> false) - let get_reachable_mem_from_globals (global_vars:varinfo list) ctx = + let get_reachable_mem_from_globals (global_vars:varinfo list) man = global_vars |> List.map (fun v -> Lval (Var v, NoOffset)) |> List.filter_map (fun exp -> - match ctx.ask (Queries.MayPointTo exp) with + match man.ask (Queries.MayPointTo exp) with | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a = 1 -> begin match List.hd @@ Queries.AD.elements a with - | Queries.AD.Addr.Addr (v, _) when (ctx.ask (Queries.IsHeapVar v)) && not (ctx.ask (Queries.IsMultiple v)) -> Some v + | Queries.AD.Addr.Addr (v, _) when (man.ask (Queries.IsHeapVar v)) && not (man.ask (Queries.IsMultiple v)) -> Some v | _ -> None end | _ -> None) - let rec get_reachable_mem_from_str_ptr_globals (global_struct_ptr_vars:varinfo list) ctx = + let rec get_reachable_mem_from_str_ptr_globals (global_struct_ptr_vars:varinfo list) man = let eval_value_of_heap_var heap_var = - match ctx.ask (Queries.EvalValue (Lval (Var heap_var, NoOffset))) with + match man.ask (Queries.EvalValue (Lval (Var heap_var, NoOffset))) with | a when not (Queries.VD.is_top a) -> begin match a with | Struct s -> @@ -75,7 +75,7 @@ struct let reachable_from_addr_set = Queries.AD.fold (fun addr acc -> match addr with - | Queries.AD.Addr.Addr (v, _) -> (v :: get_reachable_mem_from_str_ptr_globals [v] ctx) @ acc + | Queries.AD.Addr.Addr (v, _) -> (v :: get_reachable_mem_from_str_ptr_globals [v] man) @ acc | _ -> acc ) a [] in @@ -89,27 +89,27 @@ struct | _ -> [] in let get_pts_of_non_heap_ptr_var var = - match ctx.ask (Queries.MayPointTo (Lval (Var var, NoOffset))) with + match man.ask (Queries.MayPointTo (Lval (Var var, NoOffset))) with | a when not (Queries.AD.is_top a) && Queries.AD.cardinal a = 1 -> begin match List.hd @@ Queries.AD.elements a with - | Queries.AD.Addr.Addr (v, _) when (ctx.ask (Queries.IsHeapVar v)) && not (ctx.ask (Queries.IsMultiple v)) -> v :: (eval_value_of_heap_var v) - | Queries.AD.Addr.Addr (v, _) when not (ctx.ask (Queries.IsAllocVar v)) && isPointerType v.vtype -> get_reachable_mem_from_str_ptr_globals [v] ctx + | Queries.AD.Addr.Addr (v, _) when (man.ask (Queries.IsHeapVar v)) && not (man.ask (Queries.IsMultiple v)) -> v :: (eval_value_of_heap_var v) + | Queries.AD.Addr.Addr (v, _) when not (man.ask (Queries.IsAllocVar v)) && isPointerType v.vtype -> get_reachable_mem_from_str_ptr_globals [v] man | _ -> [] end | _ -> [] in global_struct_ptr_vars |> List.fold_left (fun acc var -> - if ctx.ask (Queries.IsHeapVar var) then (eval_value_of_heap_var var) @ acc - else if not (ctx.ask (Queries.IsAllocVar var)) && isPointerType var.vtype then (get_pts_of_non_heap_ptr_var var) @ acc + if man.ask (Queries.IsHeapVar var) then (eval_value_of_heap_var var) @ acc + else if not (man.ask (Queries.IsAllocVar var)) && isPointerType var.vtype then (get_pts_of_non_heap_ptr_var var) @ acc else acc ) [] - let get_reachable_mem_from_str_non_ptr_globals (global_struct_non_ptr_vars:varinfo list) ctx = + let get_reachable_mem_from_str_non_ptr_globals (global_struct_non_ptr_vars:varinfo list) man = global_struct_non_ptr_vars (* Filter out global struct vars that don't have pointer fields *) |> List.filter_map (fun v -> - match ctx.ask (Queries.EvalValue (Lval (Var v, NoOffset))) with + match man.ask (Queries.EvalValue (Lval (Var v, NoOffset))) with | a when not (Queries.VD.is_top a) -> begin match a with | Queries.VD.Struct s -> @@ -129,7 +129,7 @@ struct Queries.AD.fold (fun addr acc_addr -> match addr with | Queries.AD.Addr.Addr (v, _) -> - let reachable_from_v = Queries.AD.of_list (List.map (fun v -> Queries.AD.Addr.Addr (v, `NoOffset)) (get_reachable_mem_from_str_ptr_globals [v] ctx)) in + let reachable_from_v = Queries.AD.of_list (List.map (fun v -> Queries.AD.Addr.Addr (v, `NoOffset)) (get_reachable_mem_from_str_ptr_globals [v] man)) in Queries.AD.join (Queries.AD.add addr reachable_from_v) acc_addr | _ -> acc_addr ) a (Queries.AD.empty ()) @@ -140,29 +140,29 @@ struct reachable_from_fields @ acc_struct ) [] - let warn_for_multi_threaded_due_to_abort ctx = - let malloc_called = was_malloc_called ctx in - if not (must_be_single_threaded ctx ~since_start:true) && malloc_called then ( + let warn_for_multi_threaded_due_to_abort man = + let malloc_called = was_malloc_called man in + if not (must_be_single_threaded man ~since_start:true) && malloc_called then ( set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Program aborted while running in multi-threaded mode. A memory leak might occur" ) (* If [is_return] is set to [true], then a thread return occurred, else a thread exit *) - let warn_for_thread_return_or_exit ctx is_return = - if not (ToppedVarInfoSet.is_empty ctx.local) then ( + let warn_for_thread_return_or_exit man is_return = + if not (ToppedVarInfoSet.is_empty man.local) then ( set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; - let current_thread = ctx.ask (Queries.CurrentThreadId) in + let current_thread = man.ask (Queries.CurrentThreadId) in M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory may be leaked at thread %s for thread %a" (if is_return then "return" else "exit") ThreadIdDomain.ThreadLifted.pretty current_thread ) - let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) ctx = - let allocated_mem = ctx.local in + let check_for_mem_leak ?(assert_exp_imprecise = false) ?(exp = None) man = + let allocated_mem = man.local in if not (D.is_empty allocated_mem) then - let reachable_mem_from_non_struct_globals = D.of_list (get_reachable_mem_from_globals (get_global_vars ()) ctx) in - let reachable_mem_from_struct_ptr_globals = D.of_list (get_reachable_mem_from_str_ptr_globals (get_global_struct_ptr_vars ()) ctx) in - let reachable_mem_from_struct_non_ptr_globals = D.of_list (get_reachable_mem_from_str_non_ptr_globals (get_global_struct_non_ptr_vars ()) ctx) in + let reachable_mem_from_non_struct_globals = D.of_list (get_reachable_mem_from_globals (get_global_vars ()) man) in + let reachable_mem_from_struct_ptr_globals = D.of_list (get_reachable_mem_from_str_ptr_globals (get_global_struct_ptr_vars ()) man) in + let reachable_mem_from_struct_non_ptr_globals = D.of_list (get_reachable_mem_from_str_non_ptr_globals (get_global_struct_non_ptr_vars ()) man) in let reachable_mem_from_struct_globals = D.join reachable_mem_from_struct_ptr_globals reachable_mem_from_struct_non_ptr_globals in let reachable_mem = D.join reachable_mem_from_non_struct_globals reachable_mem_from_struct_globals in (* Check and warn if there's unreachable allocated memory at program exit *) @@ -181,72 +181,72 @@ struct M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Memory leak detected for heap variables" (* TRANSFER FUNCTIONS *) - let return ctx (exp:exp option) (f:fundec) : D.t = + let return man (exp:exp option) (f:fundec) : D.t = (* Check for a valid-memcleanup and memtrack violation in a multi-threaded setting *) (* The check for multi-threadedness is to ensure that valid-memtrack and valid-memclenaup are treated separately for single-threaded programs *) - if (ctx.ask (Queries.MayBeThreadReturn) && not (must_be_single_threaded ctx ~since_start:true)) then ( - warn_for_thread_return_or_exit ctx true + if (man.ask (Queries.MayBeThreadReturn) && not (must_be_single_threaded man ~since_start:true)) then ( + warn_for_thread_return_or_exit man true ); (* Returning from "main" is one possible program exit => need to check for memory leaks *) if f.svar.vname = "main" then ( - check_for_mem_leak ctx; - if not (must_be_single_threaded ctx ~since_start:false) && was_malloc_called ctx then begin + check_for_mem_leak man; + if not (must_be_single_threaded man ~since_start:false) && was_malloc_called man then begin set_mem_safety_flag InvalidMemTrack; set_mem_safety_flag InvalidMemcleanup; M.warn ~category:(Behavior (Undefined MemoryLeak)) ~tags:[CWE 401] "Possible memory leak: Memory was allocated in a multithreaded program, but not all threads are joined." end ); - ctx.local + man.local - let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = - let state = ctx.local in + let special man (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = + let state = man.local in let desc = LibraryFunctions.find f in match desc.special arglist with | Malloc _ | Calloc _ | Realloc _ -> - ctx.sideg () true; - begin match ctx.ask (Queries.AllocVar {on_stack = false}) with + man.sideg () true; + begin match man.ask (Queries.AllocVar {on_stack = false}) with | `Lifted var -> ToppedVarInfoSet.add var state | _ -> state end | Free ptr -> - begin match ctx.ask (Queries.MayPointTo ptr) with + begin match man.ask (Queries.MayPointTo ptr) with | ad when (not (Queries.AD.is_top ad)) && Queries.AD.cardinal ad = 1 -> (* Note: Need to always set "ana.malloc.unique_address_count" to a value > 0 *) begin match Queries.AD.choose ad with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) && ctx.ask (Queries.IsHeapVar v) && not @@ ctx.ask (Queries.IsMultiple v) -> - ToppedVarInfoSet.remove v ctx.local - | _ -> ctx.local + | Queries.AD.Addr.Addr (v,_) when man.ask (Queries.IsAllocVar v) && man.ask (Queries.IsHeapVar v) && not @@ man.ask (Queries.IsMultiple v) -> + ToppedVarInfoSet.remove v man.local + | _ -> man.local end - | _ -> ctx.local + | _ -> man.local end | Abort -> - check_for_mem_leak ctx; + check_for_mem_leak man; (* Upon a call to the "Abort" special function in the multi-threaded case, we give up and conservatively warn *) - warn_for_multi_threaded_due_to_abort ctx; + warn_for_multi_threaded_due_to_abort man; state | Assert { exp; _ } -> - begin match ctx.ask (Queries.EvalInt exp) with + begin match man.ask (Queries.EvalInt exp) with | a when Queries.ID.is_bot a -> M.warn ~category:Assert "assert expression %a is bottom" d_exp exp | a -> begin match Queries.ID.to_bool a with | Some true -> () | Some false -> (* If we know for sure that the expression in "assert" is false => need to check for memory leaks *) - warn_for_multi_threaded_due_to_abort ctx; - check_for_mem_leak ctx + warn_for_multi_threaded_due_to_abort man; + check_for_mem_leak man | None -> - warn_for_multi_threaded_due_to_abort ctx; - check_for_mem_leak ctx ~assert_exp_imprecise:true ~exp:(Some exp) + warn_for_multi_threaded_due_to_abort man; + check_for_mem_leak man ~assert_exp_imprecise:true ~exp:(Some exp) end end; state | ThreadExit _ -> - begin match ctx.ask (Queries.CurrentThreadId) with + begin match man.ask (Queries.CurrentThreadId) with | `Lifted tid -> - warn_for_thread_return_or_exit ctx false + warn_for_thread_return_or_exit man false | _ -> () end; state @@ -255,7 +255,7 @@ struct let startstate v = D.bot () let exitstate v = D.top () - let threadenter ctx ~multiple lval f args = [D.bot ()] + let threadenter man ~multiple lval f args = [D.bot ()] end let _ = diff --git a/src/analyses/memOutOfBounds.ml b/src/analyses/memOutOfBounds.ml index 914644a86e..296a990b80 100644 --- a/src/analyses/memOutOfBounds.ml +++ b/src/analyses/memOutOfBounds.ml @@ -22,7 +22,7 @@ struct module D = Lattice.Unit include Analyses.ValueContexts(D) - let context ctx _ _ = () + let context man _ _ = () let name () = "memOutOfBounds" @@ -69,21 +69,21 @@ struct in host_contains_a_ptr host || offset_contains_a_ptr offset - let points_to_alloc_only ctx ptr = - match ctx.ask (Queries.MayPointTo ptr) with + let points_to_alloc_only man ptr = + match man.ask (Queries.MayPointTo ptr) with | a when not (Queries.AD.is_top a)-> Queries.AD.for_all (function - | Addr (v, o) -> ctx.ask (Queries.IsAllocVar v) + | Addr (v, o) -> man.ask (Queries.IsAllocVar v) | _ -> false ) a | _ -> false - let get_size_of_ptr_target ctx ptr = - if points_to_alloc_only ctx ptr then + let get_size_of_ptr_target man ptr = + if points_to_alloc_only man ptr then (* Ask for BlobSize from the base address (the second component being set to true) in order to avoid BlobSize giving us bot *) - ctx.ask (Queries.BlobSize {exp = ptr; base_address = true}) + man.ask (Queries.BlobSize {exp = ptr; base_address = true}) else - match ctx.ask (Queries.MayPointTo ptr) with + match man.ask (Queries.MayPointTo ptr) with | a when not (Queries.AD.is_top a) -> let pts_list = Queries.AD.elements a in let pts_elems_to_sizes (addr: Queries.AD.elt) = @@ -96,7 +96,7 @@ struct begin match v.vtype with | TArray (item_typ, _, _) -> let item_typ_size_in_bytes = size_of_type_in_bytes item_typ in - begin match ctx.ask (Queries.EvalLength ptr) with + begin match man.ask (Queries.EvalLength ptr) with | `Lifted arr_len -> let arr_len_casted = ID.cast_to (Cilfacade.ptrdiff_ikind ()) arr_len in begin @@ -131,8 +131,8 @@ struct | TPtr (t, _) -> Some t | _ -> None - let eval_ptr_offset_in_binop ctx exp ptr_contents_typ = - let eval_offset = ctx.ask (Queries.EvalInt exp) in + let eval_ptr_offset_in_binop man exp ptr_contents_typ = + let eval_offset = man.ask (Queries.EvalInt exp) in let ptr_contents_typ_size_in_bytes = size_of_type_in_bytes ptr_contents_typ in match eval_offset with | `Lifted eo -> @@ -165,7 +165,7 @@ struct with IntDomain.ArithmeticOnIntegerBot _ -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () end - let cil_offs_to_idx ctx typ offs = + let cil_offs_to_idx man typ offs = (* TODO: Some duplication with convert_offset in base.ml, unclear how to immediately get more reuse *) let rec convert_offset (ofs: offset) = match ofs with @@ -174,7 +174,7 @@ struct | Index (exp, ofs) when CilType.Exp.equal exp (Lazy.force Offset.Index.Exp.any) -> (* special offset added by convertToQueryLval *) `Index (ID.top (), convert_offset ofs) | Index (exp, ofs) -> - let i = match ctx.ask (Queries.EvalInt exp) with + let i = match man.ask (Queries.EvalInt exp) with | `Lifted x -> x | _ -> ID.top_of @@ Cilfacade.ptrdiff_ikind () in @@ -183,9 +183,9 @@ struct PreValueDomain.Offs.to_index (convert_offset offs) - let check_unknown_addr_deref ctx ptr = + let check_unknown_addr_deref man ptr = let may_contain_unknown_addr = - match ctx.ask (Queries.EvalValue ptr) with + match man.ask (Queries.EvalValue ptr) with | a when not (Queries.VD.is_top a) -> begin match a with | Address a -> ValueDomain.AD.may_be_unknown a @@ -199,8 +199,8 @@ struct M.warn ~category:(Behavior (Undefined Other)) "Pointer %a contains an unknown address. Invalid dereference may occur" d_exp ptr end - let ptr_only_has_str_addr ctx ptr = - match ctx.ask (Queries.EvalValue ptr) with + let ptr_only_has_str_addr man ptr = + match man.ask (Queries.EvalValue ptr) with | a when not (Queries.VD.is_top a) -> begin match a with | Address a -> ValueDomain.AD.for_all (fun addr -> match addr with | StrPtr _ -> true | _ -> false) a @@ -209,8 +209,8 @@ struct (* Intuition: if ptr evaluates to top, it could all sorts of things and not only string addresses *) | _ -> false - let rec get_addr_offs ctx ptr = - match ctx.ask (Queries.MayPointTo ptr) with + let rec get_addr_offs man ptr = + match man.ask (Queries.MayPointTo ptr) with | a when not (VDQ.AD.is_top a) -> let ptr_deref_type = get_ptr_deref_type @@ typeOf ptr in begin match ptr_deref_type with @@ -254,22 +254,22 @@ struct M.warn "Pointer %a has a points-to-set of top. An invalid memory access might occur" d_exp ptr; ID.top_of @@ Cilfacade.ptrdiff_ikind () - and check_lval_for_oob_access ctx ?(is_implicitly_derefed = false) lval = + and check_lval_for_oob_access man ?(is_implicitly_derefed = false) lval = (* If the lval does not contain a pointer or if it does contain a pointer, but only points to string addresses, then no need to WARN *) - if (not @@ lval_contains_a_ptr lval) || ptr_only_has_str_addr ctx (Lval lval) then () + if (not @@ lval_contains_a_ptr lval) || ptr_only_has_str_addr man (Lval lval) then () else (* If the lval doesn't indicate an explicit dereference, we still need to check for an implicit dereference *) (* An implicit dereference is, e.g., printf("%p", ptr), where ptr is a pointer *) match lval, is_implicitly_derefed with | (Var _, _), false -> () - | (Var v, _), true -> check_no_binop_deref ctx (Lval lval) + | (Var v, _), true -> check_no_binop_deref man (Lval lval) | (Mem e, o), _ -> let ptr_deref_type = get_ptr_deref_type @@ typeOf e in let offs_intdom = begin match ptr_deref_type with - | Some t -> cil_offs_to_idx ctx t o + | Some t -> cil_offs_to_idx man t o | None -> ID.bot_of @@ Cilfacade.ptrdiff_ikind () end in - let e_size = get_size_of_ptr_target ctx e in + let e_size = get_size_of_ptr_target man e in let () = begin match e_size with | `Top -> (set_mem_safety_flag InvalidDeref; @@ -300,20 +300,20 @@ struct end end in begin match e with - | Lval (Var v, _) as lval_exp -> check_no_binop_deref ctx lval_exp + | Lval (Var v, _) as lval_exp -> check_no_binop_deref man lval_exp | BinOp (binop, e1, e2, t) when binop = PlusPI || binop = MinusPI || binop = IndexPI -> - check_binop_exp ctx binop e1 e2 t; - check_exp_for_oob_access ctx ~is_implicitly_derefed e1; - check_exp_for_oob_access ctx ~is_implicitly_derefed e2 - | _ -> check_exp_for_oob_access ctx ~is_implicitly_derefed e + check_binop_exp man binop e1 e2 t; + check_exp_for_oob_access man ~is_implicitly_derefed e1; + check_exp_for_oob_access man ~is_implicitly_derefed e2 + | _ -> check_exp_for_oob_access man ~is_implicitly_derefed e end - and check_no_binop_deref ctx lval_exp = - check_unknown_addr_deref ctx lval_exp; + and check_no_binop_deref man lval_exp = + check_unknown_addr_deref man lval_exp; let behavior = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in - let ptr_size = get_size_of_ptr_target ctx lval_exp in - let addr_offs = get_addr_offs ctx lval_exp in + let ptr_size = get_size_of_ptr_target man lval_exp in + let addr_offs = get_addr_offs man lval_exp in let ptr_type = typeOf lval_exp in let ptr_contents_type = get_ptr_deref_type ptr_type in match ptr_contents_type with @@ -341,7 +341,7 @@ struct end | _ -> M.error "Expression %a is not a pointer" d_exp lval_exp - and check_exp_for_oob_access ctx ?(is_implicitly_derefed = false) exp = + and check_exp_for_oob_access man ?(is_implicitly_derefed = false) exp = match exp with | Const _ | SizeOf _ @@ -353,20 +353,20 @@ struct | SizeOfE e | AlignOfE e | UnOp (_, e, _) - | CastE (_, e) -> check_exp_for_oob_access ctx ~is_implicitly_derefed e + | CastE (_, e) -> check_exp_for_oob_access man ~is_implicitly_derefed e | BinOp (bop, e1, e2, t) -> - check_exp_for_oob_access ctx ~is_implicitly_derefed e1; - check_exp_for_oob_access ctx ~is_implicitly_derefed e2 + check_exp_for_oob_access man ~is_implicitly_derefed e1; + check_exp_for_oob_access man ~is_implicitly_derefed e2 | Question (e1, e2, e3, _) -> - check_exp_for_oob_access ctx ~is_implicitly_derefed e1; - check_exp_for_oob_access ctx ~is_implicitly_derefed e2; - check_exp_for_oob_access ctx ~is_implicitly_derefed e3 + check_exp_for_oob_access man ~is_implicitly_derefed e1; + check_exp_for_oob_access man ~is_implicitly_derefed e2; + check_exp_for_oob_access man ~is_implicitly_derefed e3 | Lval lval | StartOf lval - | AddrOf lval -> check_lval_for_oob_access ctx ~is_implicitly_derefed lval + | AddrOf lval -> check_lval_for_oob_access man ~is_implicitly_derefed lval - and check_binop_exp ctx binop e1 e2 t = - check_unknown_addr_deref ctx e1; + and check_binop_exp man binop e1 e2 t = + check_unknown_addr_deref man e1; let binopexp = BinOp (binop, e1, e2, t) in let behavior = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in @@ -374,13 +374,13 @@ struct | PlusPI | IndexPI | MinusPI -> - let ptr_size = get_size_of_ptr_target ctx e1 in - let addr_offs = get_addr_offs ctx e1 in + let ptr_size = get_size_of_ptr_target man e1 in + let addr_offs = get_addr_offs man e1 in let ptr_type = typeOf e1 in let ptr_contents_type = get_ptr_deref_type ptr_type in begin match ptr_contents_type with | Some t -> - let offset_size = eval_ptr_offset_in_binop ctx e2 t in + let offset_size = eval_ptr_offset_in_binop man e2 t in (* Make sure to add the address offset to the binop offset *) let offset_size_with_addr_size = match offset_size with | `Lifted os -> @@ -425,12 +425,12 @@ struct | _ -> () (* For memset() and memcpy() *) - let check_count ctx fun_name ptr n = + let check_count man fun_name ptr n = let (behavior:MessageCategory.behavior) = Undefined MemoryOutOfBoundsAccess in let cwe_number = 823 in - let ptr_size = get_size_of_ptr_target ctx ptr in - let eval_n = ctx.ask (Queries.EvalInt n) in - let addr_offs = get_addr_offs ctx ptr in + let ptr_size = get_size_of_ptr_target man ptr in + let eval_n = man.ask (Queries.EvalInt n) in + let addr_offs = get_addr_offs man ptr in match ptr_size, eval_n with | `Top, _ -> set_mem_safety_flag InvalidDeref; @@ -462,20 +462,20 @@ struct (* TRANSFER FUNCTIONS *) - let assign ctx (lval:lval) (rval:exp) : D.t = - check_lval_for_oob_access ctx lval; - check_exp_for_oob_access ctx rval; - ctx.local + let assign man (lval:lval) (rval:exp) : D.t = + check_lval_for_oob_access man lval; + check_exp_for_oob_access man rval; + man.local - let branch ctx (exp:exp) (tv:bool) : D.t = - check_exp_for_oob_access ctx exp; - ctx.local + let branch man (exp:exp) (tv:bool) : D.t = + check_exp_for_oob_access man exp; + man.local - let return ctx (exp:exp option) (f:fundec) : D.t = - Option.iter (fun x -> check_exp_for_oob_access ctx x) exp; - ctx.local + let return man (exp:exp option) (f:fundec) : D.t = + Option.iter (fun x -> check_exp_for_oob_access man x) exp; + man.local - let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in let is_arg_implicitly_derefed arg = let read_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = false } arglist in @@ -484,23 +484,23 @@ struct let write_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in List.mem arg read_shallow_args || List.mem arg read_deep_args || List.mem arg write_shallow_args || List.mem arg write_deep_args in - Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; - List.iter (fun arg -> check_exp_for_oob_access ctx ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) arg) arglist; + Option.iter (fun x -> check_lval_for_oob_access man x) lval; + List.iter (fun arg -> check_exp_for_oob_access man ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) arg) arglist; (* Check calls to memset and memcpy for out-of-bounds-accesses *) match desc.special arglist with - | Memset { dest; ch; count; } -> check_count ctx f.vname dest count; + | Memset { dest; ch; count; } -> check_count man f.vname dest count; | Memcpy { dest; src; n = count; } -> - (check_count ctx f.vname src count; - check_count ctx f.vname dest count;) - | _ -> ctx.local + (check_count man f.vname src count; + check_count man f.vname dest count;) + | _ -> man.local - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - List.iter (fun arg -> check_exp_for_oob_access ctx arg) args; - [ctx.local, ctx.local] + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + List.iter (fun arg -> check_exp_for_oob_access man arg) args; + [man.local, man.local] - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = - Option.iter (fun x -> check_lval_for_oob_access ctx x) lval; - ctx.local + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = + Option.iter (fun x -> check_lval_for_oob_access man x) lval; + man.local let startstate v = () let exitstate v = () diff --git a/src/analyses/modifiedSinceSetjmp.ml b/src/analyses/modifiedSinceSetjmp.ml index 515f63cdb4..85d3180990 100644 --- a/src/analyses/modifiedSinceSetjmp.ml +++ b/src/analyses/modifiedSinceSetjmp.ml @@ -30,42 +30,42 @@ struct ) ls (VS.empty ()) (* transfer functions *) - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - [ctx.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *) + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + [man.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *) - let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = + let combine_env man lval fexp f args fc au (f_ask: Queries.ask) = let taintedcallee = relevants_from_ad (f_ask.f Queries.MayBeTainted) in - add_to_all_defined taintedcallee ctx.local + add_to_all_defined taintedcallee man.local - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask) : D.t = - ctx.local + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask) : D.t = + man.local - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in match desc.special arglist with | Setjmp _ -> - let entry = (ctx.prev_node, ctx.control_context ()) in - let v = D.find entry ctx.local in (* Will make bot binding explicit here *) + let entry = (man.prev_node, man.control_context ()) in + let v = D.find entry man.local in (* Will make bot binding explicit here *) (* LHS of setjmp not marked as tainted on purpose *) - D.add entry v ctx.local + D.add entry v man.local | _ -> - ctx.local + man.local let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] + let threadenter man ~multiple lval f args = [D.bot ()] let exitstate v = D.top () - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with - | Queries.MayBeModifiedSinceSetjmp entry -> D.find entry ctx.local + | Queries.MayBeModifiedSinceSetjmp entry -> D.find entry man.local | _ -> Queries.Result.top q - let event ctx (e: Events.t) octx = + let event man (e: Events.t) oman = match e with | Access {ad; kind = Write; _} -> - add_to_all_defined (relevants_from_ad ad) ctx.local + add_to_all_defined (relevants_from_ad ad) man.local | _ -> - ctx.local + man.local end let _ = diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 3710d2db9c..824c36814f 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -119,13 +119,13 @@ struct let create_protected protected = `Lifted2 protected end - let add ctx ((addr, rw): AddrRW.t): D.t = + let add man ((addr, rw): AddrRW.t): D.t = match addr with | Addr ((v, o) as mv) -> - let (s, m) = ctx.local in + let (s, m) = man.local in let s' = MustLocksetRW.add_mval_rw (mv, rw) s in let m' = - match ctx.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) with + match man.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) with | `Lifted Recursive -> MustMultiplicity.increment mv m | `Lifted NonRec -> if MustLocksetRW.mem_mval mv s then @@ -136,23 +136,23 @@ struct (s', m') | NullPtr -> M.warn "locking NULL mutex"; - ctx.local + man.local | StrPtr _ - | UnknownPtr -> ctx.local + | UnknownPtr -> man.local - let remove' ctx ~warn (addr: Addr.t): D.t = + let remove' man ~warn (addr: Addr.t): D.t = match addr with | StrPtr _ - | UnknownPtr -> ctx.local + | UnknownPtr -> man.local | NullPtr -> if warn then M.warn "unlocking NULL mutex"; - ctx.local + man.local | Addr mv -> - let (s, m) = ctx.local in + let (s, m) = man.local in if warn && not (MustLocksetRW.mem_mval mv s) then M.warn "unlocking mutex (%a) which may not be held" Mval.pretty mv; - if MutexTypeAnalysis.must_be_recursive ctx mv then ( + if MutexTypeAnalysis.must_be_recursive man mv then ( let (m', rmed) = MustMultiplicity.decrement mv m in if rmed then (* TODO: don't repeat the same semantic_equal checks *) @@ -166,10 +166,10 @@ struct let remove = remove' ~warn:true - let remove_all ctx: D.t = + let remove_all man: D.t = (* Mutexes.iter (fun m -> - ctx.emit (MustUnlock m) - ) (D.export_locks ctx.local); *) + man.emit (MustUnlock m) + ) (D.export_locks man.local); *) (* TODO: used to have remove_nonspecial, which kept v.vname.[0] = '{' variables *) M.warn "unlocking unknown mutex which may not be held"; D.empty () @@ -196,17 +196,17 @@ struct num_mutexes := 0; sum_protected := 0 - let query (ctx: (D.t, _, _, V.t) ctx) (type a) (q: a Queries.t): a Queries.result = - let ls, m = ctx.local in + let query (man: (D.t, _, _, V.t) man) (type a) (q: a Queries.t): a Queries.result = + let ls, m = man.local in (* get the set of mutexes protecting the variable v in the given mode *) - let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (ctx.global (V.protecting v))) in + let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (man.global (V.protecting v))) in match q with | Queries.MayBePublic _ when MustLocksetRW.is_all ls -> false | Queries.MayBePublic {global=v; write; protection} -> let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) - (* if Mutexes.mem verifier_atomic (Lockset.export_locks ctx.local) then + (* if Mutexes.mem verifier_atomic (Lockset.export_locks man.local) then false else *) MustLockset.disjoint held_locks protecting @@ -215,7 +215,7 @@ struct let held_locks = MustLockset.remove without_mutex (MustLocksetRW.to_must_lockset ls) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) - (* if Mutexes.mem verifier_atomic (Lockset.export_locks (Lockset.remove (without_mutex, true) ctx.local)) then + (* if Mutexes.mem verifier_atomic (Lockset.export_locks (Lockset.remove (without_mutex, true) man.local)) then false else *) MustLockset.disjoint held_locks protecting @@ -235,7 +235,7 @@ struct let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in MustLockset.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *) | Queries.MustProtectedVars {mutex; write} -> - let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected mutex))) in + let protected = GProtected.get ~write Strong (G.protected (man.global (V.protected mutex))) in VarSet.fold (fun v acc -> Queries.VS.add v acc ) protected (Queries.VS.empty ()) @@ -246,13 +246,13 @@ struct begin match g with | `Left g' -> (* protecting *) if GobConfig.get_bool "dbg.print_protection" then ( - let protecting = GProtecting.get ~write:false Strong (G.protecting (ctx.global g)) in (* readwrite protecting *) + let protecting = GProtecting.get ~write:false Strong (G.protecting (man.global g)) in (* readwrite protecting *) let s = MustLockset.cardinal protecting in M.info_noloc ~category:Race "Variable %a read-write protected by %d mutex(es): %a" CilType.Varinfo.pretty g' s MustLockset.pretty protecting ) | `Right m -> (* protected *) if GobConfig.get_bool "dbg.print_protection" then ( - let protected = GProtected.get ~write:false Strong (G.protected (ctx.global g)) in (* readwrite protected *) + let protected = GProtected.get ~write:false Strong (G.protected (man.global g)) in (* readwrite protected *) let s = VarSet.cardinal protected in max_protected := max !max_protected s; sum_protected := !sum_protected + s; @@ -279,21 +279,21 @@ struct let should_print ls = not (is_empty ls) end - let access ctx (a: Queries.access) = - fst ctx.local + let access man (a: Queries.access) = + fst man.local - let event (ctx: (D.t, _, _, V.t) ctx) e (octx: (D.t, _, _, _) ctx) = + let event (man: (D.t, _, _, V.t) man) e (oman: (D.t, _, _, _) man) = match e with - | Events.Access {exp; ad; kind; _} when ThreadFlag.has_ever_been_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) - let is_recovered_to_st = not (ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx)) in - (* must use original (pre-assign, etc) ctx queries *) + | Events.Access {exp; ad; kind; _} when ThreadFlag.has_ever_been_multi (Analyses.ask_of_man man) -> (* threadflag query in post-threadspawn man *) + let is_recovered_to_st = not (ThreadFlag.is_currently_multi (Analyses.ask_of_man man)) in + (* must use original (pre-assign, etc) man queries *) let old_access var_opt = - (* TODO: this used to use ctx instead of octx, why? *) + (* TODO: this used to use man instead of oman, why? *) (*privatization*) match var_opt with | Some v -> - if not (MustLocksetRW.is_all (fst octx.local)) then - let locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd (fst octx.local)) in + if not (MustLocksetRW.is_all (fst oman.local)) then + let locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd (fst oman.local)) in let write = match kind with | Write | Free -> true | Read -> false @@ -301,23 +301,23 @@ struct | Spawn -> false (* TODO: nonsense? *) in let s = GProtecting.make ~write ~recovered:is_recovered_to_st locks in - ctx.sideg (V.protecting v) (G.create_protecting s); + man.sideg (V.protecting v) (G.create_protecting s); if !AnalysisState.postsolving then ( - let protecting mode = GProtecting.get ~write mode (G.protecting (ctx.global (V.protecting v))) in + let protecting mode = GProtecting.get ~write mode (G.protecting (man.global (V.protecting v))) in let held_strong = protecting Strong in let held_weak = protecting Weak in let vs = VarSet.singleton v in let protected = G.create_protected @@ GProtected.make ~write vs in - MustLockset.iter (fun ml -> ctx.sideg (V.protected ml) protected) held_strong; + MustLockset.iter (fun ml -> man.sideg (V.protected ml) protected) held_strong; (* If the mutex set here is top, it is actually not accessed *) if is_recovered_to_st && not @@ MustLockset.is_all held_weak then - MustLockset.iter (fun ml -> ctx.sideg (V.protected ml) protected) held_weak; + MustLockset.iter (fun ml -> man.sideg (V.protected ml) protected) held_weak; ) | None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound." in let module AD = Queries.AD in - let has_escaped g = octx.ask (Queries.MayEscape g) in + let has_escaped g = oman.ask (Queries.MayEscape g) in let on_ad ad = let f = function | AD.Addr.Addr (g,_) when g.vglob || has_escaped g -> old_access (Some g) @@ -333,7 +333,7 @@ struct | ad -> (* the case where the points-to set is non top and contains unknown values *) (* now we need to access all fields that might be pointed to: is this correct? *) - begin match octx.ask (ReachableUkTypes exp) with + begin match oman.ask (ReachableUkTypes exp) with | ts when Queries.TS.is_top ts -> () | ts -> @@ -348,9 +348,9 @@ struct (* | _ -> old_access None None *) (* TODO: what about this case? *) end; - ctx.local + man.local | _ -> - event ctx e octx (* delegate to must lockset analysis *) + event man e oman (* delegate to must lockset analysis *) let finalize () = if GobConfig.get_bool "dbg.print_protection" then ( diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index 5ea0afc809..0e318aad8b 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -17,7 +17,7 @@ struct let eval_exp_addr (a: Queries.ask) exp = a.f (Queries.MayPointTo exp) - let lock ctx rw may_fail nonzero_return_when_aquired a lv_opt arg = + let lock man rw may_fail nonzero_return_when_aquired a lv_opt arg = let compute_refine_split (e: Addr.t) = match e with | Addr a -> let arg_e = AddrOf (PreValueDomain.Mval.to_cil a) in @@ -31,57 +31,57 @@ struct match lv_opt with | None -> Queries.AD.iter (fun e -> - ctx.split () (Events.Lock (e, rw) :: compute_refine_split e) + man.split () (Events.Lock (e, rw) :: compute_refine_split e) ) (eval_exp_addr a arg); if may_fail then - ctx.split () []; + man.split () []; raise Analyses.Deadcode | Some lv -> let sb = Events.SplitBranch (Lval lv, nonzero_return_when_aquired) in Queries.AD.iter (fun e -> - ctx.split () (sb :: Events.Lock (e, rw) :: compute_refine_split e); + man.split () (sb :: Events.Lock (e, rw) :: compute_refine_split e); ) (eval_exp_addr a arg); if may_fail then ( let fail_exp = if nonzero_return_when_aquired then Lval lv else BinOp(Gt, Lval lv, zero, intType) in - ctx.split () [Events.SplitBranch (fail_exp, not nonzero_return_when_aquired)] + man.split () [Events.SplitBranch (fail_exp, not nonzero_return_when_aquired)] ); raise Analyses.Deadcode - let return ctx exp fundec : D.t = + let return man exp fundec : D.t = (* deprecated but still valid SV-COMP convention for atomic block *) if get_bool "ana.sv-comp.functions" && String.starts_with fundec.svar.vname ~prefix:"__VERIFIER_atomic_" then - ctx.emit (Events.Unlock (LockDomain.Addr.of_var LF.verifier_atomic_var)) + man.emit (Events.Unlock (LockDomain.Addr.of_var LF.verifier_atomic_var)) - let body ctx f : D.t = + let body man f : D.t = (* deprecated but still valid SV-COMP convention for atomic block *) if get_bool "ana.sv-comp.functions" && String.starts_with f.svar.vname ~prefix:"__VERIFIER_atomic_" then - ctx.emit (Events.Lock (LockDomain.Addr.of_var LF.verifier_atomic_var, true)) + man.emit (Events.Lock (LockDomain.Addr.of_var LF.verifier_atomic_var, true)) - let special (ctx: (unit, _, _, _) ctx) lv f arglist : D.t = + let special (man: (unit, _, _, _) man) lv f arglist : D.t = let remove_rw x = x in let unlock arg remove_fn = Queries.AD.iter (fun e -> - ctx.split () [Events.Unlock (remove_fn e)] - ) (eval_exp_addr (Analyses.ask_of_ctx ctx) arg); + man.split () [Events.Unlock (remove_fn e)] + ) (eval_exp_addr (Analyses.ask_of_man man) arg); raise Analyses.Deadcode in let desc = LF.find f in match desc.special arglist with | Lock { lock = arg; try_ = failing; write = rw; return_on_success = nonzero_return_when_aquired } -> - lock ctx rw failing nonzero_return_when_aquired (Analyses.ask_of_ctx ctx) lv arg + lock man rw failing nonzero_return_when_aquired (Analyses.ask_of_man man) lv arg | Unlock arg -> unlock arg remove_rw | Wait { mutex = m_arg; _} | TimedWait { mutex = m_arg; _} -> (* mutex is unlocked while waiting but relocked when returns *) (* emit unlock-lock events for privatization *) - let ms = eval_exp_addr (Analyses.ask_of_ctx ctx) m_arg in + let ms = eval_exp_addr (Analyses.ask_of_man man) m_arg in Queries.AD.iter (fun m -> (* unlock-lock each possible mutex as a split to be dependent *) (* otherwise may-point-to {a, b} might unlock a, but relock b *) - ctx.split () [Events.Unlock m; Events.Lock (m, true)]; + man.split () [Events.Unlock m; Events.Lock (m, true)]; ) ms; raise Deadcode (* splits cover all cases *) | _ -> diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml index 3deec3ef59..98fd33d621 100644 --- a/src/analyses/mutexGhosts.ml +++ b/src/analyses/mutexGhosts.ml @@ -66,70 +66,70 @@ struct | Addr mv when LockDomain.Mval.is_definite mv -> Some (LockDomain.MustLock.of_mval mv) | _ -> None - let event ctx e octx = + let event man e oman = let verifier_atomic_addr = LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var in begin match e with | Events.Lock (l, _) when not (LockDomain.Addr.equal l verifier_atomic_addr) -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ())); + man.sideg (V.node man.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ())); if !AnalysisState.postsolving then ( - ctx.sideg V.update (G.create_update (NodeSet.singleton ctx.prev_node)); - let (locked, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in + man.sideg V.update (G.create_update (NodeSet.singleton man.prev_node)); + let (locked, _, _) = G.node (man.global (V.node man.prev_node)) in if Locked.cardinal locked > 1 then ( Locked.iter (fun lock -> Option.iter (fun lock -> - ctx.sideg (V.lock lock) (G.create_lock true) + man.sideg (V.lock lock) (G.create_lock true) ) (mustlock_of_addr lock) ) locked ); ) | Events.Unlock l when not (LockDomain.Addr.equal l verifier_atomic_addr) -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ())); + man.sideg (V.node man.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ())); if !AnalysisState.postsolving then ( - ctx.sideg V.update (G.create_update (NodeSet.singleton ctx.prev_node)); - let (_, unlocked, _) = G.node (ctx.global (V.node ctx.prev_node)) in + man.sideg V.update (G.create_update (NodeSet.singleton man.prev_node)); + let (_, unlocked, _) = G.node (man.global (V.node man.prev_node)) in if Locked.cardinal unlocked > 1 then ( Locked.iter (fun lock -> Option.iter (fun lock -> - ctx.sideg (V.lock lock) (G.create_lock true) + man.sideg (V.lock lock) (G.create_lock true) ) (mustlock_of_addr lock) ) unlocked ); ) | Events.EnterMultiThreaded -> - ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true)); + man.sideg (V.node man.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true)); if !AnalysisState.postsolving then - ctx.sideg V.update (G.create_update (NodeSet.singleton ctx.prev_node)); + man.sideg V.update (G.create_update (NodeSet.singleton man.prev_node)); | _ -> () end; - ctx.local + man.local - let threadspawn ctx ~multiple lval f args octx = - ctx.sideg V.threadcreate (G.create_threadcreate (NodeSet.singleton ctx.node)); - ctx.local + let threadspawn man ~multiple lval f args oman = + man.sideg V.threadcreate (G.create_threadcreate (NodeSet.singleton man.node)); + man.local - let ghost_var_available ctx = function - | WitnessGhost.Var.Locked ((v, o) as lock) -> not (Offset.Z.contains_index o) && not (G.lock (ctx.global (V.lock lock))) + let ghost_var_available man = function + | WitnessGhost.Var.Locked ((v, o) as lock) -> not (Offset.Z.contains_index o) && not (G.lock (man.global (V.lock lock))) | Multithreaded -> true - let ghost_var_available ctx v = - WitnessGhost.enabled () && ghost_var_available ctx v + let ghost_var_available man v = + WitnessGhost.enabled () && ghost_var_available man v module VariableSet = Set.Make (YamlWitnessType.GhostInstrumentation.Variable) - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with - | GhostVarAvailable v -> ghost_var_available ctx v + | GhostVarAvailable v -> ghost_var_available man v | YamlEntryGlobal (g, task) -> let g: V.t = Obj.obj g in begin match g with | `Right true when YamlWitness.entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type -> - let nodes = G.update (ctx.global g) in + let nodes = G.update (man.global g) in let (variables, location_updates) = NodeSet.fold (fun node (variables, location_updates) -> - let (locked, unlocked, multithread) = G.node (ctx.global (V.node node)) in + let (locked, unlocked, multithread) = G.node (man.global (V.node node)) in let variables' = Locked.fold (fun l acc -> match mustlock_of_addr l with - | Some l when ghost_var_available ctx (Locked l) -> + | Some l when ghost_var_available man (Locked l) -> let variable = WitnessGhost.variable' (Locked l) in VariableSet.add variable acc | _ -> @@ -139,7 +139,7 @@ struct let updates = Locked.fold (fun l acc -> match mustlock_of_addr l with - | Some l when ghost_var_available ctx (Locked l) -> + | Some l when ghost_var_available man (Locked l) -> let update = WitnessGhost.update' (Locked l) GoblintCil.one in update :: acc | _ -> @@ -149,7 +149,7 @@ struct let updates = Unlocked.fold (fun l acc -> match mustlock_of_addr l with - | Some l when ghost_var_available ctx (Locked l) -> + | Some l when ghost_var_available man (Locked l) -> let update = WitnessGhost.update' (Locked l) GoblintCil.zero in update :: acc | _ -> @@ -158,7 +158,7 @@ struct in let (variables', updates) = if not (GobConfig.get_bool "exp.earlyglobs") && multithread then ( - if ghost_var_available ctx Multithreaded then ( + if ghost_var_available man Multithreaded then ( let variable = WitnessGhost.variable' Multithreaded in let update = WitnessGhost.update' Multithreaded GoblintCil.one in let variables' = VariableSet.add variable variables' in @@ -183,7 +183,7 @@ struct | `Middle _ -> Queries.Result.top q | `Right _ -> Queries.Result.top q end - | InvariantGlobalNodes -> (G.threadcreate (ctx.global V.threadcreate): NodeSet.t) + | InvariantGlobalNodes -> (G.threadcreate (man.global V.threadcreate): NodeSet.t) | _ -> Queries.Result.top q end diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 4a993bbd7d..06d1b1ff6e 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -24,7 +24,7 @@ struct module O = Offset.Unit (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = + let assign man (lval:lval) (rval:exp) : D.t = match lval with | (Var v, o) -> (* There's no way to use the PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP etc for accesses via pointers *) @@ -35,8 +35,8 @@ struct | Const (CInt (c, _, _)) -> MAttr.of_int c | _ -> `Top) in - ctx.sideg (v,o) kind; - ctx.local + man.sideg (v,o) kind; + man.local | Field ({fname = "__sig"; _}, NoOffset) when ValueDomain.Compound.is_mutex_type t -> (* OSX *) let kind: MAttr.t = match Cil.constFold true rval with | Const (CInt (c, _, _)) -> @@ -48,42 +48,42 @@ struct end | _ -> `Top in - ctx.sideg (v,o) kind; - ctx.local + man.sideg (v,o) kind; + man.local | Index (i,o') -> let o'' = O.of_offs (`Index (i, `NoOffset)) in helper (O.add_offset o o'') (Cilfacade.typeOffset t (Index (i,NoOffset))) o' | Field (f,o') -> let o'' = O.of_offs (`Field (f, `NoOffset)) in helper (O.add_offset o o'') (Cilfacade.typeOffset t (Field (f,NoOffset))) o' - | NoOffset -> ctx.local + | NoOffset -> man.local in helper `NoOffset v.vtype o - | _ -> ctx.local + | _ -> man.local - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LF.find f in match desc.special arglist with | MutexInit {mutex = mutex; attr = attr} -> - let attr = ctx.ask (Queries.EvalMutexAttr attr) in - let mutexes = ctx.ask (Queries.MayPointTo mutex) in + let attr = man.ask (Queries.EvalMutexAttr attr) in + let mutexes = man.ask (Queries.MayPointTo mutex) in (* It is correct to iter over these sets here, as mutexes need to be intialized before being used, and an analysis that detects usage before initialization is a different analysis. *) Queries.AD.iter (function addr -> match addr with - | Queries.AD.Addr.Addr (v,o) -> ctx.sideg (v,O.of_offs o) attr + | Queries.AD.Addr.Addr (v,o) -> man.sideg (v,O.of_offs o) attr | _ -> () ) mutexes; - ctx.local - | _ -> ctx.local + man.local + | _ -> man.local - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with - | Queries.MutexType (v,o) -> (ctx.global (v,o):MutexAttrDomain.t) + | Queries.MutexType (v,o) -> (man.global (v,o):MutexAttrDomain.t) | _ -> Queries.Result.top q end -let must_be_recursive ctx (v,o) = - ctx.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) = `Lifted MutexAttrDomain.MutexKind.Recursive +let must_be_recursive man (v,o) = + man.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) = `Lifted MutexAttrDomain.MutexKind.Recursive let _ = MCP.register_analysis (module Spec : MCPSpec) diff --git a/src/analyses/poisonVariables.ml b/src/analyses/poisonVariables.ml index e8a4a9a404..1b0c159dc0 100644 --- a/src/analyses/poisonVariables.ml +++ b/src/analyses/poisonVariables.ml @@ -26,26 +26,26 @@ struct (* transfer functions *) - let return ctx (exp:exp option) (f:fundec) : D.t = + let return man (exp:exp option) (f:fundec) : D.t = (* remove locals, except ones which need to be weakly updated*) - if D.is_top ctx.local then - ctx.local + if D.is_top man.local then + man.local else ( let locals = f.sformals @ f.slocals in D.filter (fun v -> not (List.exists (fun local -> - CilType.Varinfo.equal v local && not (ctx.ask (Queries.IsMultiple local)) + CilType.Varinfo.equal v local && not (man.ask (Queries.IsMultiple local)) ) locals) - ) ctx.local + ) man.local ) - let enter ctx (_:lval option) (_:fundec) (args:exp list) : (D.t * D.t) list = - if VS.is_empty ctx.local then - [ctx.local,ctx.local] + let enter man (_:lval option) (_:fundec) (args:exp list) : (D.t * D.t) list = + if VS.is_empty man.local then + [man.local,man.local] else ( - let reachable_from_args = List.fold (fun ad e -> Queries.AD.join ad (ctx.ask (ReachableFrom e))) (Queries.AD.empty ()) args in - if Queries.AD.is_top reachable_from_args || VS.is_top ctx.local then - [ctx.local, ctx.local] + let reachable_from_args = List.fold (fun ad e -> Queries.AD.join ad (man.ask (ReachableFrom e))) (Queries.AD.empty ()) args in + if Queries.AD.is_top reachable_from_args || VS.is_top man.local then + [man.local, man.local] else let reachable_vars = let get_vars addr vs = @@ -55,53 +55,53 @@ struct in Queries.AD.fold get_vars reachable_from_args (VS.empty ()) in - [VS.diff ctx.local reachable_vars, VS.inter reachable_vars ctx.local] + [VS.diff man.local reachable_vars, VS.inter reachable_vars man.local] ) - let combine_env ctx lval fexp f args fc au f_ask = - VS.join au ctx.local + let combine_env man lval fexp f args fc au f_ask = + VS.join au man.local let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] + let threadenter man ~multiple lval f args = [D.bot ()] let exitstate v = D.top () - let event ctx e octx = + let event man e oman = match e with | Events.Longjmped {lval} -> - let modified_locals = ctx.ask (MayBeModifiedSinceSetjmp (ctx.prev_node, ctx.control_context ())) in + let modified_locals = man.ask (MayBeModifiedSinceSetjmp (man.prev_node, man.control_context ())) in let modified_locals = match lval with | Some (Var v, NoOffset) -> Queries.VS.remove v modified_locals | _ -> modified_locals (* Does usually not really occur, if it does, this is sound *) in - let (_, longjmp_nodes) = ctx.ask ActiveJumpBuf in + let (_, longjmp_nodes) = man.ask ActiveJumpBuf in JmpBufDomain.NodeSet.iter (fun longjmp_node -> if Queries.VS.is_top modified_locals then - M.info ~category:(Behavior (Undefined Other)) ~loc:(Node longjmp_node) "Since setjmp at %a, potentially all locals were modified! Reading them will yield Undefined Behavior." Node.pretty ctx.prev_node + M.info ~category:(Behavior (Undefined Other)) ~loc:(Node longjmp_node) "Since setjmp at %a, potentially all locals were modified! Reading them will yield Undefined Behavior." Node.pretty man.prev_node else if not (Queries.VS.is_empty modified_locals) then - M.info ~category:(Behavior (Undefined Other)) ~loc:(Node longjmp_node) "Since setjmp at %a, locals %a were modified! Reading them will yield Undefined Behavior." Node.pretty ctx.prev_node Queries.VS.pretty modified_locals + M.info ~category:(Behavior (Undefined Other)) ~loc:(Node longjmp_node) "Since setjmp at %a, locals %a were modified! Reading them will yield Undefined Behavior." Node.pretty man.prev_node Queries.VS.pretty modified_locals ) longjmp_nodes; - D.join modified_locals ctx.local + D.join modified_locals man.local | Access {ad; kind = Read; _} -> (* TODO: what about AD with both known and unknown pointers? *) begin match ad with - | ad when Queries.AD.is_top ad && not (VS.is_empty octx.local) -> + | ad when Queries.AD.is_top ad && not (VS.is_empty oman.local) -> M.warn ~category:(Behavior (Undefined Other)) "reading unknown memory location, may be tainted!" | ad -> (* Use original access state instead of current with removed written vars. *) - Queries.AD.iter (check_mval octx.local) ad + Queries.AD.iter (check_mval oman.local) ad end; - ctx.local + man.local | Access {ad; kind = Write; _} -> (* TODO: what about AD with both known and unknown pointers? *) begin match ad with | ad when Queries.AD.is_top ad -> - ctx.local + man.local | ad -> Queries.AD.fold (fun addr vs -> rem_mval vs addr - ) ad ctx.local + ) ad man.local end - | _ -> ctx.local + | _ -> man.local end diff --git a/src/analyses/pthreadSignals.ml b/src/analyses/pthreadSignals.ml index 68c2bf4f34..98f53ee0fd 100644 --- a/src/analyses/pthreadSignals.ml +++ b/src/analyses/pthreadSignals.ml @@ -22,18 +22,18 @@ struct (* transfer functions *) - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LF.find f in match desc.special arglist with | Signal cond | Broadcast cond -> - let mhp = G.singleton @@ MHP.current (Analyses.ask_of_ctx ctx) in - let publish_one a = ctx.sideg a mhp in - let possible_vars = possible_vinfos (Analyses.ask_of_ctx ctx) cond in + let mhp = G.singleton @@ MHP.current (Analyses.ask_of_man man) in + let publish_one a = man.sideg a mhp in + let possible_vars = possible_vinfos (Analyses.ask_of_man man) cond in List.iter publish_one possible_vars; - ctx.local + man.local | Wait {cond = cond; _} -> - let current_mhp = MHP.current (Analyses.ask_of_ctx ctx) in + let current_mhp = MHP.current (Analyses.ask_of_man man) in let module Signalled = struct type signalled = Never | NotConcurrently | PossiblySignalled @@ -45,7 +45,7 @@ struct | Never, Never -> Never let can_be_signalled a = - let signalling_tids = ctx.global a in + let signalling_tids = man.global a in if G.is_top signalling_tids then PossiblySignalled else if G.is_empty signalling_tids then @@ -57,23 +57,23 @@ struct end in let open Signalled in - let add_if_singleton conds = match conds with | [a] -> Signals.add (ValueDomain.Addr.of_var a) ctx.local | _ -> ctx.local in - let conds = possible_vinfos (Analyses.ask_of_ctx ctx) cond in + let add_if_singleton conds = match conds with | [a] -> Signals.add (ValueDomain.Addr.of_var a) man.local | _ -> man.local in + let conds = possible_vinfos (Analyses.ask_of_man man) cond in (match List.fold_left (fun acc cond -> can_be_signalled cond ||| acc) Never conds with | PossiblySignalled -> add_if_singleton conds | NotConcurrently -> - (M.warn ~category:Deadcode "The condition variable(s) pointed to by %a are never signalled concurrently, succeeding code is live due to spurious wakeups only!" Basetype.CilExp.pretty cond; ctx.local) + (M.warn ~category:Deadcode "The condition variable(s) pointed to by %a are never signalled concurrently, succeeding code is live due to spurious wakeups only!" Basetype.CilExp.pretty cond; man.local) | Never -> - (M.warn ~category:Deadcode "The condition variable(s) pointed to by %a are never signalled, succeeding code is live due to spurious wakeups only!" Basetype.CilExp.pretty cond; ctx.local) + (M.warn ~category:Deadcode "The condition variable(s) pointed to by %a are never signalled, succeeding code is live due to spurious wakeups only!" Basetype.CilExp.pretty cond; man.local) ) | TimedWait _ -> (* Time could simply have elapsed *) - ctx.local - | _ -> ctx.local + man.local + | _ -> man.local let startstate v = Signals.empty () - let threadenter ctx ~multiple lval f args = [ctx.local] + let threadenter man ~multiple lval f args = [man.local] let exitstate v = Signals.empty () end diff --git a/src/analyses/ptranalAnalysis.ml b/src/analyses/ptranalAnalysis.ml index 6991b5ea22..0d7f4dd228 100644 --- a/src/analyses/ptranalAnalysis.ml +++ b/src/analyses/ptranalAnalysis.ml @@ -13,7 +13,7 @@ struct let name () = "ptranal" - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | Queries.EvalFunvar (Lval (Mem e, _)) -> let funs = Ptranal.resolve_exp e in diff --git a/src/analyses/raceAnalysis.ml b/src/analyses/raceAnalysis.ml index 2f6611d467..263506b4fb 100644 --- a/src/analyses/raceAnalysis.ml +++ b/src/analyses/raceAnalysis.ml @@ -217,24 +217,24 @@ struct vulnerable := 0; unsafe := 0 - let side_vars ctx memo = + let side_vars man memo = match memo with | (`Var v, _) -> if !AnalysisState.should_warn then - ctx.sideg (V.vars v) (G.create_vars (MemoSet.singleton memo)) + man.sideg (V.vars v) (G.create_vars (MemoSet.singleton memo)) | _ -> () - let side_access ctx acc ((memoroot, offset) as memo) = + let side_access man acc ((memoroot, offset) as memo) = if !AnalysisState.should_warn then - ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton acc)))); - side_vars ctx memo + man.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.singleton acc)))); + side_vars man memo (** Side-effect empty access set for prefix-type_suffix race checking. *) - let side_access_empty ctx ((memoroot, offset) as memo) = + let side_access_empty man ((memoroot, offset) as memo) = if !AnalysisState.should_warn then - ctx.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.empty ())))); - side_vars ctx memo + man.sideg (V.access memoroot) (G.create_access (OffsetTrie.singleton offset (`Lifted (Access.AS.empty ())))); + side_vars man memo (** Get immediate type_suffix memo. *) let type_suffix_memo ((root, offset) : Access.Memo.t) : Access.Memo.t option = @@ -247,30 +247,30 @@ struct | `Type (TSArray (ts, _, _)), `Index ((), offset') -> Some (`Type ts, offset') (* (int[])[*] -> int *) | _, `Index ((), offset') -> None (* TODO: why indexing on non-array? *) - let rec find_type_suffix' ctx ((root, offset) as memo : Access.Memo.t) : Access.AS.t = - let trie = G.access (ctx.global (V.access root)) in + let rec find_type_suffix' man ((root, offset) as memo : Access.Memo.t) : Access.AS.t = + let trie = G.access (man.global (V.access root)) in let accs = match OffsetTrie.find offset trie with | `Lifted accs -> accs | `Bot -> Access.AS.empty () in - let type_suffix = find_type_suffix ctx memo in + let type_suffix = find_type_suffix man memo in Access.AS.union accs type_suffix (** Find accesses from all type_suffixes transitively. *) - and find_type_suffix ctx (memo : Access.Memo.t) : Access.AS.t = + and find_type_suffix man (memo : Access.Memo.t) : Access.AS.t = match type_suffix_memo memo with - | Some type_suffix_memo -> find_type_suffix' ctx type_suffix_memo + | Some type_suffix_memo -> find_type_suffix' man type_suffix_memo | None -> Access.AS.empty () - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal g -> let g: V.t = Obj.obj g in begin match g with | `Left g' -> (* accesses *) (* Logs.debug "WarnGlobal %a" Access.MemoRoot.pretty g'; *) - let trie = G.access (ctx.global g) in + let trie = G.access (man.global g) in (** Distribute access to contained fields. *) let rec distribute_inner offset (accs, children) ~prefix ~type_suffix_prefix = let accs = @@ -278,7 +278,7 @@ struct | `Lifted accs -> accs | `Bot -> Access.AS.empty () in - let type_suffix = find_type_suffix ctx (g', offset) in + let type_suffix = find_type_suffix man (g', offset) in if not (Access.AS.is_empty accs) || (not (Access.AS.is_empty prefix) && not (Access.AS.is_empty type_suffix)) then ( let memo = (g', offset) in let mem_loc_str = GobPretty.sprint Access.Memo.pretty memo in @@ -299,29 +299,29 @@ struct | IterSysVars (Global g, vf) -> MemoSet.iter (fun v -> vf (Obj.repr (V.access v)) - ) (G.vars (ctx.global (V.vars g))) + ) (G.vars (man.global (V.vars g))) | _ -> Queries.Result.top q - let event ctx e octx = + let event man e oman = match e with - | Events.Access {exp; ad; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) -> (* threadflag query in post-threadspawn ctx *) - (* must use original (pre-assign, etc) ctx queries *) + | Events.Access {exp; ad; kind; reach} when ThreadFlag.is_currently_multi (Analyses.ask_of_man man) -> (* threadflag query in post-threadspawn man *) + (* must use original (pre-assign, etc) man queries *) let conf = 110 in let module AD = Queries.AD in let part_access (vo:varinfo option): MCPAccess.A.t = (*partitions & locks*) - Obj.obj (octx.ask (PartAccess (Memory {exp; var_opt=vo; kind}))) + Obj.obj (oman.ask (PartAccess (Memory {exp; var_opt=vo; kind}))) in let node = Option.get !Node.current_node in let add_access conf voffs = let acc = part_access (Option.map fst voffs) in - Access.add ~side:(side_access octx {conf; kind; node; exp; acc}) ~side_empty:(side_access_empty octx) exp voffs; + Access.add ~side:(side_access oman {conf; kind; node; exp; acc}) ~side_empty:(side_access_empty oman) exp voffs; in let add_access_struct conf ci = let acc = part_access None in - Access.add_one ~side:(side_access octx {conf; kind; node; exp; acc}) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) + Access.add_one ~side:(side_access oman {conf; kind; node; exp; acc}) (`Type (TSComp (ci.cstruct, ci.cname, [])), `NoOffset) in - let has_escaped g = octx.ask (Queries.MayEscape g) in + let has_escaped g = oman.ask (Queries.MayEscape g) in (* The following function adds accesses to the lval-set ls -- this is the common case if we have a sound points-to set. *) let on_ad ad includes_uk = @@ -345,7 +345,7 @@ struct (* the case where the points-to set is non top and contains unknown values *) let includes_uk = ref false in (* now we need to access all fields that might be pointed to: is this correct? *) - begin match octx.ask (ReachableUkTypes exp) with + begin match oman.ask (ReachableUkTypes exp) with | ts when Queries.TS.is_top ts -> includes_uk := true | ts -> @@ -362,23 +362,23 @@ struct (* | _ -> add_access (conf - 60) None *) (* TODO: what about this case? *) end; - ctx.local + man.local | _ -> - ctx.local + man.local - let special ctx (lvalOpt: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lvalOpt: lval option) (f:varinfo) (arglist:exp list) : D.t = (* perform shallow and deep invalidate according to Library descriptors *) let desc = LibraryFunctions.find f in - if List.mem LibraryDesc.ThreadUnsafe desc.attrs && ThreadFlag.is_currently_multi (Analyses.ask_of_ctx ctx) then ( + if List.mem LibraryDesc.ThreadUnsafe desc.attrs && ThreadFlag.is_currently_multi (Analyses.ask_of_man man) then ( let exp = Lval (Var f, NoOffset) in let conf = 110 in let kind = AccessKind.Call in let node = Option.get !Node.current_node in let vo = Some f in - let acc = Obj.obj (ctx.ask (PartAccess (Memory {exp; var_opt=vo; kind}))) in - side_access ctx {conf; kind; node; exp; acc} ((`Var f), `NoOffset) ; + let acc = Obj.obj (man.ask (PartAccess (Memory {exp; var_opt=vo; kind}))) in + side_access man {conf; kind; node; exp; acc} ((`Var f), `NoOffset) ; ); - ctx.local + man.local let finalize () = let total = !safe + !unsafe + !vulnerable in diff --git a/src/analyses/region.ml b/src/analyses/region.ml index e53dded304..7cc8d34111 100644 --- a/src/analyses/region.ml +++ b/src/analyses/region.ml @@ -42,20 +42,20 @@ struct | `Top -> false | `Bot -> true - let get_region ctx e = - let regpart = ctx.global () in - if is_bullet e regpart ctx.local then + let get_region man e = + let regpart = man.global () in + if is_bullet e regpart man.local then None else - Some (regions e regpart ctx.local) + Some (regions e regpart man.local) (* queries *) - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | Queries.Regions e -> - let regpart = ctx.global () in - if is_bullet e regpart ctx.local then Queries.Result.bot q (* TODO: remove bot *) else - let ls = List.fold_right Queries.LS.add (regions e regpart ctx.local) (Queries.LS.empty ()) in + let regpart = man.global () in + if is_bullet e regpart man.local then Queries.Result.bot q (* TODO: remove bot *) else + let ls = List.fold_right Queries.LS.add (regions e regpart man.local) (Queries.LS.empty ()) in ls | _ -> Queries.Result.top q @@ -76,7 +76,7 @@ struct | Some r when Lvals.is_empty r -> false | _ -> true end - let access ctx (a: Queries.access) = + let access man (a: Queries.access) = match a with | Point -> Some (Lvals.empty ()) @@ -84,30 +84,30 @@ struct (* TODO: remove regions that cannot be reached from the var*) (* forget specific indices *) (* TODO: If indices are topped, could they not be collected in the first place? *) - Option.map (Lvals.of_list % List.map (Tuple2.map2 Offset.Exp.top_indices)) (get_region ctx e) + Option.map (Lvals.of_list % List.map (Tuple2.map2 Offset.Exp.top_indices)) (get_region man e) (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - match ctx.local with + let assign man (lval:lval) (rval:exp) : D.t = + match man.local with | `Lifted reg -> - let old_regpart = ctx.global () in + let old_regpart = man.global () in let regpart, reg = Reg.assign lval rval (old_regpart, reg) in if not (RegPart.leq regpart old_regpart) then - ctx.sideg () regpart; + man.sideg () regpart; `Lifted reg | x -> x - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local + let branch man (exp:exp) (tv:bool) : D.t = + man.local - let body ctx (f:fundec) : D.t = - ctx.local + let body man (f:fundec) : D.t = + man.local - let return ctx (exp:exp option) (f:fundec) : D.t = + let return man (exp:exp option) (f:fundec) : D.t = let locals = f.sformals @ f.slocals in - match ctx.local with + match man.local with | `Lifted reg -> - let old_regpart = ctx.global () in + let old_regpart = man.global () in let regpart, reg = match exp with | Some exp -> Reg.assign (ReturnUtil.return_lval ()) exp (old_regpart, reg) @@ -115,74 +115,74 @@ struct in let regpart, reg = Reg.kill_vars locals (Reg.remove_vars locals (regpart, reg)) in if not (RegPart.leq regpart old_regpart) then - ctx.sideg () regpart; + man.sideg () regpart; `Lifted reg | x -> x - let enter ctx (lval: lval option) (fundec:fundec) (args:exp list) : (D.t * D.t) list = + let enter man (lval: lval option) (fundec:fundec) (args:exp list) : (D.t * D.t) list = let rec fold_right2 f xs ys r = match xs, ys with | x::xs, y::ys -> f x y (fold_right2 f xs ys r) | _ -> r in - match ctx.local with + match man.local with | `Lifted reg -> let f x r reg = Reg.assign (var x) r reg in - let old_regpart = ctx.global () in + let old_regpart = man.global () in let regpart, reg = fold_right2 f fundec.sformals args (old_regpart,reg) in if not (RegPart.leq regpart old_regpart) then - ctx.sideg () regpart; - [ctx.local, `Lifted reg] + man.sideg () regpart; + [man.local, `Lifted reg] | x -> [x,x] - let combine_env ctx lval fexp f args fc au f_ask = - ctx.local + let combine_env man lval fexp f args fc au f_ask = + man.local - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = match au with | `Lifted reg -> begin - let old_regpart = ctx.global () in + let old_regpart = man.global () in let regpart, reg = match lval with | None -> (old_regpart, reg) | Some lval -> Reg.assign lval (AddrOf (ReturnUtil.return_lval ())) (old_regpart, reg) in let regpart, reg = Reg.remove_vars [ReturnUtil.return_varinfo ()] (regpart, reg) in if not (RegPart.leq regpart old_regpart) then - ctx.sideg () regpart; + man.sideg () regpart; `Lifted reg end | _ -> au - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in match desc.special arglist with | Malloc _ | Calloc _ | Realloc _ | Alloca _ -> begin - match ctx.local, lval with + match man.local, lval with | `Lifted reg, Some lv -> - let old_regpart = ctx.global () in + let old_regpart = man.global () in (* TODO: should realloc use arg region if failed/in-place? *) let regpart, reg = Reg.assign_bullet lv (old_regpart, reg) in if not (RegPart.leq regpart old_regpart) then - ctx.sideg () regpart; + man.sideg () regpart; `Lifted reg - | _ -> ctx.local + | _ -> man.local end | _ -> - ctx.local + man.local let startstate v = `Lifted (RegMap.bot ()) - let threadenter ctx ~multiple lval f args = + let threadenter man ~multiple lval f args = [`Lifted (RegMap.bot ())] - let threadspawn ctx ~multiple lval f args fctx = - match ctx.local with + let threadspawn man ~multiple lval f args fman = + match man.local with | `Lifted reg -> - let old_regpart = ctx.global () in + let old_regpart = man.global () in let regpart, reg = List.fold_right Reg.assign_escape args (old_regpart, reg) in if not (RegPart.leq regpart old_regpart) then - ctx.sideg () regpart; + man.sideg () regpart; `Lifted reg | x -> x diff --git a/src/analyses/stackTrace.ml b/src/analyses/stackTrace.ml index 56656c0639..8e6149d802 100644 --- a/src/analyses/stackTrace.ml +++ b/src/analyses/stackTrace.ml @@ -13,14 +13,14 @@ struct (* transfer functions *) - let body ctx (f:fundec) : D.t = - if f.svar.vname = "__goblint_dummy_init" then ctx.local else D.push f.svar ctx.local + let body man (f:fundec) : D.t = + if f.svar.vname = "__goblint_dummy_init" then man.local else D.push f.svar man.local - let combine_env ctx lval fexp f args fc au f_ask = - ctx.local (* keep local as opposed to IdentitySpec *) + let combine_env man lval fexp f args fc au f_ask = + man.local (* keep local as opposed to IdentitySpec *) let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] + let threadenter man ~multiple lval f args = [D.bot ()] let exitstate v = D.top () end @@ -34,17 +34,17 @@ struct (* transfer functions *) - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - [ctx.local, D.push !Goblint_tracing.current_loc ctx.local] + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + [man.local, D.push !Goblint_tracing.current_loc man.local] - let combine_env ctx lval fexp f args fc au f_ask = - ctx.local (* keep local as opposed to IdentitySpec *) + let combine_env man lval fexp f args fc au f_ask = + man.local (* keep local as opposed to IdentitySpec *) let startstate v = D.bot () let exitstate v = D.top () - let threadenter ctx ~multiple lval f args = - [D.push !Goblint_tracing.current_loc ctx.local] + let threadenter man ~multiple lval f args = + [D.push !Goblint_tracing.current_loc man.local] end diff --git a/src/analyses/symbLocks.ml b/src/analyses/symbLocks.ml index ab116c525d..0850fac317 100644 --- a/src/analyses/symbLocks.ml +++ b/src/analyses/symbLocks.ml @@ -27,12 +27,12 @@ struct let name () = "symb_locks" let startstate v = D.top () - let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadenter man ~multiple lval f args = [D.top ()] + let threadspawn man ~multiple lval f args fman = man.local let exitstate v = D.top () - let branch ctx exp tv = ctx.local - let body ctx f = ctx.local + let branch man exp tv = man.local + let body man f = man.local let invalidate_exp ask exp st = D.filter (fun e -> not (VarEq.may_change ask exp e)) st @@ -40,14 +40,14 @@ struct let invalidate_lval ask lv st = invalidate_exp ask (mkAddrOf lv) st - let assign ctx lval rval = invalidate_lval (Analyses.ask_of_ctx ctx) lval ctx.local + let assign man lval rval = invalidate_lval (Analyses.ask_of_man man) lval man.local - let return ctx exp fundec = - List.fold_right D.remove_var (fundec.sformals@fundec.slocals) ctx.local + let return man exp fundec = + List.fold_right D.remove_var (fundec.sformals@fundec.slocals) man.local - let enter ctx lval f args = [(ctx.local,ctx.local)] - let combine_env ctx lval fexp f args fc au f_ask = au - let combine_assign ctx lval fexp f args fc st2 f_ask = ctx.local + let enter man lval f args = [(man.local,man.local)] + let combine_env man lval fexp f args fc au f_ask = au + let combine_assign man lval fexp f args fc st2 f_ask = man.local let get_locks e st = let add_perel x xs = @@ -82,24 +82,24 @@ struct | Some (_, i, e) -> D.fold (lock_index i e) slocks (PS.empty ()) | _ -> PS.empty () - let special ctx lval f arglist = + let special man lval f arglist = let desc = LF.find f in match desc.special arglist, f.vname with | Lock { lock; _ }, _ -> - D.add (Analyses.ask_of_ctx ctx) lock ctx.local + D.add (Analyses.ask_of_man man) lock man.local | Unlock lock, _ -> - D.remove (Analyses.ask_of_ctx ctx) lock ctx.local + D.remove (Analyses.ask_of_man man) lock man.local | _, _ -> let st = match lval with - | Some lv -> invalidate_lval (Analyses.ask_of_ctx ctx) lv ctx.local - | None -> ctx.local + | Some lv -> invalidate_lval (Analyses.ask_of_man man) lv man.local + | None -> man.local in let write_args = LibraryDesc.Accesses.find_kind desc.accs Write arglist in (* TODO: why doesn't invalidate_exp involve any reachable for deep write? *) - List.fold_left (fun st e -> invalidate_exp (Analyses.ask_of_ctx ctx) e st) st write_args + List.fold_left (fun st e -> invalidate_exp (Analyses.ask_of_man man) e st) st write_args module A = @@ -125,7 +125,7 @@ struct let should_print lp = not (is_empty lp) end - let add_per_element_access ctx e rw = + let add_per_element_access man e rw = (* Per-element returns a triple of exps, first are the "element" pointers, in the second and third positions are the respectively access and mutex. Access and mutex expressions have exactly the given "elements" as "prefixes". @@ -160,14 +160,14 @@ struct xs in let do_perel e xs = - match get_all_locks (Analyses.ask_of_ctx ctx) e ctx.local with + match get_all_locks (Analyses.ask_of_man man) e man.local with | a when not (PS.is_top a || PS.is_empty a) -> PS.fold one_perelem a xs | _ -> xs in let do_lockstep e xs = - match same_unknown_index (Analyses.ask_of_ctx ctx) e ctx.local with + match same_unknown_index (Analyses.ask_of_man man) e man.local with | a when not (PS.is_top a || PS.is_empty a) -> PS.fold one_lockstep a xs @@ -175,11 +175,11 @@ struct in let matching_exps = Queries.ES.meet - (match ctx.ask (Queries.EqualSet e) with + (match man.ask (Queries.EqualSet e) with | es when not (Queries.ES.is_top es || Queries.ES.is_empty es) -> Queries.ES.add e es | _ -> Queries.ES.singleton e) - (match ctx.ask (Queries.Regions e) with + (match man.ask (Queries.Regions e) with | ls when not (Queries.LS.is_top ls || Queries.LS.is_empty ls) -> let add_exp x xs = try Queries.ES.add (Mval.Exp.to_cil_exp x) xs @@ -192,12 +192,12 @@ struct Queries.ES.fold do_lockstep matching_exps (Queries.ES.fold do_perel matching_exps (A.empty ())) - let access ctx (a: Queries.access) = + let access man (a: Queries.access) = match a with | Point -> A.empty () | Memory {exp = e; _} -> - add_per_element_access ctx e false + add_per_element_access man e false end let _ = diff --git a/src/analyses/taintPartialContexts.ml b/src/analyses/taintPartialContexts.ml index 917a6d1644..789c783ca3 100644 --- a/src/analyses/taintPartialContexts.ml +++ b/src/analyses/taintPartialContexts.ml @@ -16,23 +16,23 @@ struct module D = AD (* Add Lval or any Lval which it may point to to the set *) - let taint_lval ctx (lval:lval) : D.t = - D.union (ctx.ask (Queries.MayPointTo (AddrOf lval))) ctx.local + let taint_lval man (lval:lval) : D.t = + D.union (man.ask (Queries.MayPointTo (AddrOf lval))) man.local (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - taint_lval ctx lval + let assign man (lval:lval) (rval:exp) : D.t = + taint_lval man lval - let return ctx (exp:exp option) (f:fundec) : D.t = + let return man (exp:exp option) (f:fundec) : D.t = (* remove locals, except ones which need to be weakly updated*) - let d = ctx.local in + let d = man.local in let d_return = if D.is_top d then d else let locals = f.sformals @ f.slocals in D.filter (function - | AD.Addr.Addr (v,_) -> not (List.exists (fun local -> CilType.Varinfo.equal v local && not (ctx.ask (Queries.IsMultiple local))) locals) + | AD.Addr.Addr (v,_) -> not (List.exists (fun local -> CilType.Varinfo.equal v local && not (man.ask (Queries.IsMultiple local))) locals) | _ -> false ) d in @@ -40,25 +40,25 @@ struct d_return - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = (* Entering a function, all globals count as untainted *) - [ctx.local, (D.bot ())] + [man.local, (D.bot ())] - let combine_env ctx lval fexp f args fc au f_ask = - if M.tracing then M.trace "taintPC" "combine for %s in TaintPC: tainted: in function: %a before call: %a" f.svar.vname D.pretty au D.pretty ctx.local; - D.union ctx.local au + let combine_env man lval fexp f args fc au f_ask = + if M.tracing then M.trace "taintPC" "combine for %s in TaintPC: tainted: in function: %a before call: %a" f.svar.vname D.pretty au D.pretty man.local; + D.union man.local au - let combine_assign ctx (lvalOpt:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = + let combine_assign man (lvalOpt:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = match lvalOpt with - | Some lv -> taint_lval ctx lv - | None -> ctx.local + | Some lv -> taint_lval man lv + | None -> man.local - let special ctx (lvalOpt: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lvalOpt: lval option) (f:varinfo) (arglist:exp list) : D.t = (* perform shallow and deep invalidate according to Library descriptors *) let d = match lvalOpt with - | Some lv -> taint_lval ctx lv - | None -> ctx.local + | Some lv -> taint_lval man lv + | None -> man.local in let desc = LibraryFunctions.find f in let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } arglist in @@ -77,24 +77,24 @@ struct deep_addrs in (* TODO: should one handle ad with unknown pointers separately like in (all) other analyses? *) - let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.MayPointTo addr))) d shallow_addrs + let d = List.fold_left (fun accD addr -> D.union accD (man.ask (Queries.MayPointTo addr))) d shallow_addrs in - let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.ReachableFrom addr))) d deep_addrs + let d = List.fold_left (fun accD addr -> D.union accD (man.ask (Queries.ReachableFrom addr))) d deep_addrs in d let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = + let threadenter man ~multiple lval f args = [D.bot ()] - let threadspawn ctx ~multiple lval f args fctx = + let threadspawn man ~multiple lval f args fman = match lval with - | Some lv -> taint_lval ctx lv - | None -> ctx.local + | Some lv -> taint_lval man lv + | None -> man.local let exitstate v = D.top () - let query ctx (type a) (q: a Queries.t) : a Queries.result = + let query man (type a) (q: a Queries.t) : a Queries.result = match q with - | MayBeTainted -> (ctx.local : Queries.AD.t) + | MayBeTainted -> (man.local : Queries.AD.t) | _ -> Queries.Result.top q end diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index 435e1a6afe..8be2bf9315 100644 --- a/src/analyses/threadAnalysis.ml +++ b/src/analyses/threadAnalysis.ml @@ -22,19 +22,19 @@ struct module P = IdentityP (D) (* transfer functions *) - let handle_thread_return ctx (exp: exp option) = - let tid = ThreadId.get_current (Analyses.ask_of_ctx ctx) in + let handle_thread_return man (exp: exp option) = + let tid = ThreadId.get_current (Analyses.ask_of_man man) in match tid with - | `Lifted tid -> ctx.sideg tid (false, TS.bot (), not (D.is_empty ctx.local)) + | `Lifted tid -> man.sideg tid (false, TS.bot (), not (D.is_empty man.local)) | _ -> () - let return ctx (exp:exp option) _ : D.t = - if ctx.ask Queries.MayBeThreadReturn then - handle_thread_return ctx exp; - ctx.local + let return man (exp:exp option) _ : D.t = + if man.ask Queries.MayBeThreadReturn then + handle_thread_return man exp; + man.local - let rec is_not_unique ctx tid = - let (rep, parents, _) = ctx.global tid in + let rec is_not_unique man tid = + let (rep, parents, _) = man.global tid in if rep then true (* repeatedly created *) else ( @@ -45,71 +45,71 @@ struct (* created by single thread *) let parent = TS.choose parents in (* created by itself thread-recursively or by a thread that is itself multiply created *) - T.equal tid parent || is_not_unique ctx parent (* equal check needed to avoid infinte self-recursion *) + T.equal tid parent || is_not_unique man parent (* equal check needed to avoid infinte self-recursion *) ) else false (* no ancestors, starting thread *) ) - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in match desc.special arglist with | ThreadJoin { thread = id; ret_var } -> (* TODO: generalize ThreadJoin like ThreadCreate *) - (let has_clean_exit tid = not (BatTuple.Tuple3.third (ctx.global tid)) in - let tids = ctx.ask (Queries.EvalThread id) in + (let has_clean_exit tid = not (BatTuple.Tuple3.third (man.global tid)) in + let tids = man.ask (Queries.EvalThread id) in let join_thread s tid = - if has_clean_exit tid && not (is_not_unique ctx tid) then + if has_clean_exit tid && not (is_not_unique man tid) then D.remove tid s else s in if TS.is_top tids - then ctx.local + then man.local else match TS.elements tids with - | [t] -> join_thread ctx.local t (* single thread *) - | _ -> ctx.local (* if several possible threads are may-joined, none are must-joined *)) + | [t] -> join_thread man.local t (* single thread *) + | _ -> man.local (* if several possible threads are may-joined, none are must-joined *)) | ThreadExit { ret_val } -> - handle_thread_return ctx (Some ret_val); - ctx.local - | _ -> ctx.local + handle_thread_return man (Some ret_val); + man.local + | _ -> man.local - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | Queries.MustBeUniqueThread -> begin - let tid = ThreadId.get_current (Analyses.ask_of_ctx ctx) in + let tid = ThreadId.get_current (Analyses.ask_of_man man) in match tid with - | `Lifted tid -> not (is_not_unique ctx tid) + | `Lifted tid -> not (is_not_unique man tid) | _ -> false end | Queries.MustBeSingleThreaded {since_start = false} -> begin - let tid = ThreadId.get_current (Analyses.ask_of_ctx ctx) in + let tid = ThreadId.get_current (Analyses.ask_of_man man) in match tid with | `Lifted tid when T.is_main tid -> (* This analysis cannot tell if we are back in single-threaded mode or never left it. *) - D.is_empty ctx.local + D.is_empty man.local | _ -> false end | _ -> Queries.Result.top q let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = - (* ctx is of creator, side-effects to denote non-uniqueness are performed in threadspawn *) + let threadenter man ~multiple lval f args = + (* man is of creator, side-effects to denote non-uniqueness are performed in threadspawn *) [D.bot ()] - let threadspawn ctx ~multiple lval f args fctx = - let creator = ThreadId.get_current (Analyses.ask_of_ctx ctx) in - let tid = ThreadId.get_current_unlift (Analyses.ask_of_ctx fctx) in - let repeated = D.mem tid ctx.local in + let threadspawn man ~multiple lval f args fman = + let creator = ThreadId.get_current (Analyses.ask_of_man man) in + let tid = ThreadId.get_current_unlift (Analyses.ask_of_man fman) in + let repeated = D.mem tid man.local in let eff = match creator with | `Lifted ctid -> (repeated || multiple, TS.singleton ctid, false) | `Top -> (true, TS.bot (), false) | `Bot -> (multiple, TS.bot (), false) in - ctx.sideg tid eff; - D.join ctx.local (D.singleton tid) + man.sideg tid eff; + D.join man.local (D.singleton tid) let exitstate v = D.bot () end diff --git a/src/analyses/threadEscape.ml b/src/analyses/threadEscape.ml index 4311e72558..2bf67f4bb9 100644 --- a/src/analyses/threadEscape.ml +++ b/src/analyses/threadEscape.ml @@ -53,30 +53,30 @@ struct if M.tracing then M.tracel "escape" "mpt %a: %a" d_exp e AD.pretty ad; D.empty () - let thread_id ctx = - ctx.ask Queries.CurrentThreadId + let thread_id man = + man.ask Queries.CurrentThreadId (** Emit an escape event: Only necessary when code has ever been multithreaded, or when about to go multithreaded. *) - let emit_escape_event ctx escaped = + let emit_escape_event man escaped = (* avoid emitting unnecessary event *) if not (D.is_empty escaped) then - ctx.emit (Events.Escape escaped) + man.emit (Events.Escape escaped) (** Side effect escapes: In contrast to the emitting the event, side-effecting is necessary in single threaded mode, since we rely on escape status in Base for passing locals reachable via globals *) - let side_effect_escape ctx escaped threadid = + let side_effect_escape man escaped threadid = let threadid = G.singleton threadid in D.iter (fun v -> - ctx.sideg v threadid) escaped + man.sideg v threadid) escaped (* queries *) - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | Queries.MayEscape v -> - let threads = ctx.global v in + let threads = man.global v in if ThreadIdSet.is_empty threads then false else begin @@ -85,7 +85,7 @@ struct (* if our own (unique) thread is started here, that is not a problem *) false | `Lifted tid -> - let threads = ctx.ask Queries.CreatedThreads in + let threads = man.ask Queries.CreatedThreads in let not_started = MHP.definitely_not_started (current, threads) tid in let possibly_started = not not_started in possibly_started @@ -98,7 +98,7 @@ struct | `Top -> true | `Bot -> false in - match ctx.ask Queries.CurrentThreadId with + match man.ask Queries.CurrentThreadId with | `Lifted current -> let possibly_started = ThreadIdSet.exists (other_possibly_started current) threads in if possibly_started then @@ -111,7 +111,7 @@ struct true else (* Check whether current unique thread has escaped the variable *) - D.mem v ctx.local + D.mem v man.local | `Top -> true | `Bot -> @@ -120,76 +120,76 @@ struct end | _ -> Queries.Result.top q - let escape_rval ctx ask (rval:exp) = + let escape_rval man ask (rval:exp) = let escaped = reachable ask rval in let escaped = D.filter (fun v -> not v.vglob) escaped in - let thread_id = thread_id ctx in - side_effect_escape ctx escaped thread_id; + let thread_id = thread_id man in + side_effect_escape man escaped thread_id; if ThreadFlag.has_ever_been_multi ask then (* avoid emitting unnecessary event *) - emit_escape_event ctx escaped; + emit_escape_event man escaped; escaped (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - let ask = Analyses.ask_of_ctx ctx in + let assign man (lval:lval) (rval:exp) : D.t = + let ask = Analyses.ask_of_man man in let vs = mpt ask (AddrOf lval) in if D.exists (fun v -> v.vglob || has_escaped ask v) vs then ( - let escaped = escape_rval ctx ask rval in - D.join ctx.local escaped + let escaped = escape_rval man ask rval in + D.join man.local escaped ) else begin - ctx.local + man.local end - let combine_assign ctx (lval:lval option) (fexp:exp) f args fc au f_ask : D.t = - let ask = Analyses.ask_of_ctx ctx in + let combine_assign man (lval:lval option) (fexp:exp) f args fc au f_ask : D.t = + let ask = Analyses.ask_of_man man in match lval with | Some lval when D.exists (fun v -> v.vglob || has_escaped ask v) (mpt ask (AddrOf lval)) -> let rval = Lval (ReturnUtil.return_lval ()) in - let escaped = escape_rval ctx f_ask rval in (* Using f_ask because the return value is only accessible in the context of that function at this point *) - D.join ctx.local escaped - | _ -> ctx.local + let escaped = escape_rval man f_ask rval in (* Using f_ask because the return value is only accessible in the context of that function at this point *) + D.join man.local escaped + | _ -> man.local - let special ctx (lval: lval option) (f:varinfo) (args:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (args:exp list) : D.t = let desc = LibraryFunctions.find f in match desc.special args, f.vname, args with | Globalize ptr, _, _ -> - let escaped = escape_rval ctx (Analyses.ask_of_ctx ctx) ptr in - D.join ctx.local escaped + let escaped = escape_rval man (Analyses.ask_of_man man) ptr in + D.join man.local escaped | _, "pthread_setspecific" , [key; pt_value] -> - let escaped = escape_rval ctx (Analyses.ask_of_ctx ctx) pt_value in - D.join ctx.local escaped - | _ -> ctx.local + let escaped = escape_rval man (Analyses.ask_of_man man) pt_value in + D.join man.local escaped + | _ -> man.local let startstate v = D.bot () let exitstate v = D.bot () - let threadenter ctx ~multiple lval f args = + let threadenter man ~multiple lval f args = [D.bot ()] - let threadspawn ctx ~multiple lval f args fctx = - D.join ctx.local @@ + let threadspawn man ~multiple lval f args fman = + D.join man.local @@ match args with | [ptc_arg] -> - (* not reusing fctx.local to avoid unnecessarily early join of extra *) - let escaped = reachable (Analyses.ask_of_ctx ctx) ptc_arg in + (* not reusing fman.local to avoid unnecessarily early join of extra *) + let escaped = reachable (Analyses.ask_of_man man) ptc_arg in let escaped = D.filter (fun v -> not v.vglob) escaped in if M.tracing then M.tracel "escape" "%a: %a" d_exp ptc_arg D.pretty escaped; - let thread_id = thread_id ctx in - emit_escape_event ctx escaped; - side_effect_escape ctx escaped thread_id; + let thread_id = thread_id man in + emit_escape_event man escaped; + side_effect_escape man escaped thread_id; escaped | _ -> D.bot () - let event ctx e octx = + let event man e oman = match e with | Events.EnterMultiThreaded -> - let escaped = ctx.local in - let thread_id = thread_id ctx in - emit_escape_event ctx escaped; - side_effect_escape ctx escaped thread_id; - ctx.local - | _ -> ctx.local + let escaped = man.local in + let thread_id = thread_id man in + emit_escape_event man escaped; + side_effect_escape man escaped thread_id; + man.local + | _ -> man.local end let _ = diff --git a/src/analyses/threadFlag.ml b/src/analyses/threadFlag.ml index e1efcaaba5..1e0ff7db9f 100644 --- a/src/analyses/threadFlag.ml +++ b/src/analyses/threadFlag.ml @@ -35,21 +35,21 @@ struct let create_tid v = Flag.get_multi () - let return ctx exp fundec = + let return man exp fundec = match fundec.svar.vname with | "__goblint_dummy_init" -> (* TODO: is this necessary? *) - Flag.join ctx.local (Flag.get_main ()) + Flag.join man.local (Flag.get_main ()) | _ -> - ctx.local + man.local - let query ctx (type a) (x: a Queries.t): a Queries.result = + let query man (type a) (x: a Queries.t): a Queries.result = match x with - | Queries.MustBeSingleThreaded _ -> not (Flag.is_multi ctx.local) (* If this analysis can tell, it is the case since the start *) - | Queries.MustBeUniqueThread -> not (Flag.is_not_main ctx.local) - | Queries.IsEverMultiThreaded -> (ctx.global () : bool) (* requires annotation to compile *) + | Queries.MustBeSingleThreaded _ -> not (Flag.is_multi man.local) (* If this analysis can tell, it is the case since the start *) + | Queries.MustBeUniqueThread -> not (Flag.is_not_main man.local) + | Queries.IsEverMultiThreaded -> (man.global () : bool) (* requires annotation to compile *) (* This used to be in base but also commented out. *) - (* | Queries.MayBePublic _ -> Flag.is_multi ctx.local *) + (* | Queries.MayBePublic _ -> Flag.is_multi man.local *) | _ -> Queries.Result.top x module A = @@ -59,19 +59,19 @@ struct let may_race m1 m2 = m1 && m2 (* kill access when single threaded *) let should_print m = not m end - let access ctx _ = - is_currently_multi (Analyses.ask_of_ctx ctx) + let access man _ = + is_currently_multi (Analyses.ask_of_man man) - let threadenter ctx ~multiple lval f args = - if not (has_ever_been_multi (Analyses.ask_of_ctx ctx)) then - ctx.emit Events.EnterMultiThreaded; + let threadenter man ~multiple lval f args = + if not (has_ever_been_multi (Analyses.ask_of_man man)) then + man.emit Events.EnterMultiThreaded; [create_tid f] - let threadspawn ctx ~multiple lval f args fctx = - ctx.sideg () true; - if not (has_ever_been_multi (Analyses.ask_of_ctx ctx)) then - ctx.emit Events.EnterMultiThreaded; - D.join ctx.local (Flag.get_main ()) + let threadspawn man ~multiple lval f args fman = + man.sideg () true; + if not (has_ever_been_multi (Analyses.ask_of_man man)) then + man.emit Events.EnterMultiThreaded; + D.join man.local (Flag.get_main ()) end let _ = diff --git a/src/analyses/threadId.ml b/src/analyses/threadId.ml index 80bab1ebf9..53d070a056 100644 --- a/src/analyses/threadId.ml +++ b/src/analyses/threadId.ml @@ -60,7 +60,7 @@ struct let name () = "threadid" - let context ctx fd ((n,current,td) as d) = + let context man fd ((n,current,td) as d) = if GobConfig.get_bool "ana.thread.context.create-edges" then d else @@ -85,15 +85,15 @@ struct | _ -> [`Lifted (Thread.threadinit v ~multiple:true)] - let is_unique ctx = - ctx.ask Queries.MustBeUniqueThread + let is_unique man = + man.ask Queries.MustBeUniqueThread - let enter ctx lval f args = - let (n, current, (td, _)) = ctx.local in - [ctx.local, (n, current, (td,TD.bot ()))] + let enter man lval f args = + let (n, current, (td, _)) = man.local in + [man.local, (n, current, (td,TD.bot ()))] - let combine_env ctx lval fexp f args fc ((n,current,(_, au_ftd)) as au) f_ask = - let (_, _, (td, ftd)) = ctx.local in + let combine_env man lval fexp f args fc ((n,current,(_, au_ftd)) as au) f_ask = + let (_, _, (td, ftd)) = man.local in if not (GobConfig.get_bool "ana.thread.context.create-edges") then (n,current,(TD.join td au_ftd, TD.join ftd au_ftd)) else @@ -104,23 +104,23 @@ struct | `Lifted current -> BatOption.map_default (ConcDomain.ThreadSet.of_list) (ConcDomain.ThreadSet.top ()) (Thread.created current td) | _ -> ConcDomain.ThreadSet.top () - let query (ctx: (D.t, _, _, _) ctx) (type a) (x: a Queries.t): a Queries.result = + let query (man: (D.t, _, _, _) man) (type a) (x: a Queries.t): a Queries.result = match x with - | Queries.CurrentThreadId -> Tuple3.second ctx.local - | Queries.CreatedThreads -> created ctx.local + | Queries.CurrentThreadId -> Tuple3.second man.local + | Queries.CreatedThreads -> created man.local | Queries.MustBeUniqueThread -> - begin match Tuple3.second ctx.local with + begin match Tuple3.second man.local with | `Lifted tid -> Thread.is_unique tid | _ -> Queries.MustBool.top () end | Queries.MustBeSingleThreaded {since_start} -> - begin match Tuple3.second ctx.local with + begin match Tuple3.second man.local with | `Lifted tid when Thread.is_main tid -> - let created = created ctx.local in + let created = created man.local in if since_start then ConcDomain.ThreadSet.is_empty created - else if ctx.ask Queries.ThreadsJoinedCleanly then - let joined = ctx.ask Queries.MustJoinedThreads in + else if man.ask Queries.ThreadsJoinedCleanly then + let joined = man.ask Queries.MustJoinedThreads in ConcDomain.ThreadSet.is_empty (ConcDomain.ThreadSet.diff created joined) else false @@ -138,28 +138,28 @@ struct let should_print = Option.is_some end - let access ctx _ = - if is_unique ctx then - let tid = Tuple3.second ctx.local in + let access man _ = + if is_unique man then + let tid = Tuple3.second man.local in Some tid else None (** get the node that identifies the current context, possibly that of a wrapper function *) - let indexed_node_for_ctx ctx = - match ctx.ask Queries.ThreadCreateIndexedNode with + let indexed_node_for_man man = + match man.ask Queries.ThreadCreateIndexedNode with | `Lifted node, count when WrapperFunctionAnalysis.ThreadCreateUniqueCount.is_top count -> node, None | `Lifted node, count -> node, Some count - | (`Bot | `Top), _ -> ctx.prev_node, None + | (`Bot | `Top), _ -> man.prev_node, None - let threadenter ctx ~multiple lval f args:D.t list = - let n, i = indexed_node_for_ctx ctx in - let+ tid = create_tid ~multiple ctx.local (n, i) f in + let threadenter man ~multiple lval f args:D.t list = + let n, i = indexed_node_for_man man in + let+ tid = create_tid ~multiple man.local (n, i) f in (`Lifted (f, n, i), tid, (TD.bot (), TD.bot ())) - let threadspawn ctx ~multiple lval f args fctx = - let (current_n, current, (td,tdl)) = ctx.local in - let v, n, i = match fctx.local with `Lifted vni, _, _ -> vni | _ -> failwith "ThreadId.threadspawn" in + let threadspawn man ~multiple lval f args fman = + let (current_n, current, (td,tdl)) = man.local in + let v, n, i = match fman.local with `Lifted vni, _, _ -> vni | _ -> failwith "ThreadId.threadspawn" in (current_n, current, (Thread.threadspawn ~multiple td n i v, Thread.threadspawn ~multiple tdl n i v)) type marshal = (Thread.t,unit) Hashtbl.t (* TODO: don't use polymorphic Hashtbl *) diff --git a/src/analyses/threadJoins.ml b/src/analyses/threadJoins.ml index eddbe184da..354c40072a 100644 --- a/src/analyses/threadJoins.ml +++ b/src/analyses/threadJoins.ml @@ -28,43 +28,43 @@ struct end (* transfer functions *) - let threadreturn ctx = - match ctx.ask CurrentThreadId with + let threadreturn man = + match man.ask CurrentThreadId with | `Lifted tid -> - let (j,joined_clean) = ctx.local in + let (j,joined_clean) = man.local in (* the current thread has been exited cleanly if all joined threads where exited cleanly, and all created threads are joined *) - let created = ctx.ask Queries.CreatedThreads in + let created = man.ask Queries.CreatedThreads in let clean = TIDs.subset created j in - ctx.sideg tid (j, joined_clean && clean) + man.sideg tid (j, joined_clean && clean) | _ -> () (* correct? *) - let return ctx (exp:exp option) (f:fundec) : D.t = - if ThreadReturn.is_current (Analyses.ask_of_ctx ctx) then threadreturn ctx; - ctx.local + let return man (exp:exp option) (f:fundec) : D.t = + if ThreadReturn.is_current (Analyses.ask_of_man man) then threadreturn man; + man.local - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in match desc.special arglist, f.vname with - | ThreadExit _, _ -> threadreturn ctx; ctx.local + | ThreadExit _, _ -> threadreturn man; man.local | ThreadJoin { thread = id; ret_var }, _ -> - let threads = ctx.ask (Queries.EvalThread id) in + let threads = man.ask (Queries.EvalThread id) in if TIDs.is_top threads then - ctx.local + man.local else ( (* all elements are known *) let threads = TIDs.elements threads in match threads with | [tid] when TID.is_unique tid-> - let (local_joined, local_clean) = ctx.local in - let (other_joined, other_clean) = ctx.global tid in + let (local_joined, local_clean) = man.local in + let (other_joined, other_clean) = man.global tid in (MustTIDs.union (MustTIDs.add tid local_joined) other_joined, local_clean && other_clean) - | _ -> ctx.local (* if multiple possible thread ids are joined, none of them is must joined *) + | _ -> man.local (* if multiple possible thread ids are joined, none of them is must joined *) (* Possible improvement: Do the intersection first, things that are must joined in all possibly joined threads are must-joined *) ) | Unknown, "__goblint_assume_join" -> let id = List.hd arglist in - let threads = ctx.ask (Queries.EvalThread id) in + let threads = man.ask (Queries.EvalThread id) in if TIDs.is_top threads then ( M.info ~category:Unsound "Unknown thread ID assume-joined, assuming ALL threads must-joined."; (MustTIDs.bot(), true) (* consider everything joined, MustTIDs is reversed so bot is All threads *) @@ -75,33 +75,33 @@ struct if List.compare_length_with threads 1 > 0 then M.info ~category:Unsound "Ambiguous thread ID assume-joined, assuming all of those threads must-joined."; List.fold_left (fun (joined, clean) tid -> - let (other_joined, other_clean) = ctx.global tid in + let (other_joined, other_clean) = man.global tid in (MustTIDs.union (MustTIDs.add tid joined) other_joined, clean && other_clean) - ) (ctx.local) threads + ) (man.local) threads ) - | _, _ -> ctx.local + | _, _ -> man.local - let threadspawn ctx ~multiple lval f args fctx = - if D.is_bot ctx.local then ( (* bot is All threads *) + let threadspawn man ~multiple lval f args fman = + if D.is_bot man.local then ( (* bot is All threads *) M.info ~category:Imprecise "Thread created while ALL threads must-joined, continuing with no threads joined."; D.top () (* top is no threads *) ) else - match ThreadId.get_current (Analyses.ask_of_ctx fctx) with + match ThreadId.get_current (Analyses.ask_of_man fman) with | `Lifted tid -> - let (j, clean) = ctx.local in + let (j, clean) = man.local in (MustTIDs.remove tid j, clean) | _ -> - ctx.local + man.local - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with - | Queries.MustJoinedThreads -> (fst ctx.local:ConcDomain.MustThreadSet.t) (* type annotation needed to avoid "would escape the scope of its equation" *) - | Queries.ThreadsJoinedCleanly -> (snd ctx.local:bool) + | Queries.MustJoinedThreads -> (fst man.local:ConcDomain.MustThreadSet.t) (* type annotation needed to avoid "would escape the scope of its equation" *) + | Queries.ThreadsJoinedCleanly -> (snd man.local:bool) | _ -> Queries.Result.top q - let combine_env ctx lval fexp f args fc au f_ask = - let (caller_joined, local_clean) = ctx.local in + let combine_env man lval fexp f args fc au f_ask = + let (caller_joined, local_clean) = man.local in let (callee_joined, callee_clean) = au in (MustTIDs.union caller_joined callee_joined, local_clean && callee_clean) diff --git a/src/analyses/threadReturn.ml b/src/analyses/threadReturn.ml index d72e2586e8..c557be40e6 100644 --- a/src/analyses/threadReturn.ml +++ b/src/analyses/threadReturn.ml @@ -17,23 +17,23 @@ struct (* transfer functions *) - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = if !AnalysisState.global_initialization then (* We are inside enter_with inside a startfun, and thus the current function retruning is the main function *) - [ctx.local, true] + [man.local, true] else - [ctx.local, false] + [man.local, false] - let combine_env ctx lval fexp f args fc au f_ask = - ctx.local (* keep local as opposed to IdentitySpec *) + let combine_env man lval fexp f args fc au f_ask = + man.local (* keep local as opposed to IdentitySpec *) let startstate v = true - let threadenter ctx ~multiple lval f args = [true] + let threadenter man ~multiple lval f args = [true] let exitstate v = D.top () - let query (ctx: (D.t, _, _, _) ctx) (type a) (x: a Queries.t): a Queries.result = + let query (man: (D.t, _, _, _) man) (type a) (x: a Queries.t): a Queries.result = match x with - | Queries.MayBeThreadReturn -> ctx.local + | Queries.MayBeThreadReturn -> man.local | _ -> Queries.Result.top x end diff --git a/src/analyses/tmpSpecial.ml b/src/analyses/tmpSpecial.ml index 78056e3857..fd2b6f71e3 100644 --- a/src/analyses/tmpSpecial.ml +++ b/src/analyses/tmpSpecial.ml @@ -21,22 +21,22 @@ struct D.filter (fun _ (ml, deps) -> (Deps.for_all (fun arg -> not (VarEq.may_change ask exp_w arg)) deps)) st (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = + let assign man (lval:lval) (rval:exp) : D.t = if M.tracing then M.tracel "tmpSpecial" "assignment of %a" d_lval lval; (* Invalidate all entrys from the map that are possibly written by the assignment *) - invalidate (Analyses.ask_of_ctx ctx) (mkAddrOf lval) ctx.local + invalidate (Analyses.ask_of_man man) (mkAddrOf lval) man.local - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = (* For now we only track relationships intraprocedurally. *) - [ctx.local, D.bot ()] + [man.local, D.bot ()] - let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = + let combine man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) f_ask : D.t = (* For now we only track relationships intraprocedurally. *) D.bot () - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - let d = ctx.local in - let ask = Analyses.ask_of_ctx ctx in + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let d = man.local in + let ask = Analyses.ask_of_man man in (* Just dbg prints *) (if M.tracing then @@ -55,7 +55,7 @@ struct (* same for lval assignment of the call*) let d = match lval with - | Some lv -> invalidate ask (mkAddrOf lv) ctx.local + | Some lv -> invalidate ask (mkAddrOf lv) man.local | None -> d in @@ -77,16 +77,16 @@ struct d - let query ctx (type a) (q: a Queries.t) : a Queries.result = + let query man (type a) (q: a Queries.t) : a Queries.result = match q with - | TmpSpecial lv -> let ml = fst (D.find lv ctx.local) in + | TmpSpecial lv -> let ml = fst (D.find lv man.local) in if ML.is_bot ml then Queries.Result.top q else ml | _ -> Queries.Result.top q let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.bot ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadenter man ~multiple lval f args = [D.bot ()] + let threadspawn man ~multiple lval f args fman = man.local let exitstate v = D.bot () end diff --git a/src/analyses/tutorials/constants.ml b/src/analyses/tutorials/constants.ml index 0c7d801df7..5a5a8f7051 100644 --- a/src/analyses/tutorials/constants.ml +++ b/src/analyses/tutorials/constants.ml @@ -49,29 +49,29 @@ struct | _ -> I.top () (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = + let assign man (lval:lval) (rval:exp) : D.t = match get_local lval with - | Some loc -> D.add loc (eval ctx.local rval) ctx.local - | None -> ctx.local + | Some loc -> D.add loc (eval man.local rval) man.local + | None -> man.local - let branch ctx (exp:exp) (tv:bool) : D.t = - let v = eval ctx.local exp in + let branch man (exp:exp) (tv:bool) : D.t = + let v = eval man.local exp in match I.to_bool v with | Some b when b <> tv -> raise Deadcode (* if the expression evaluates to not tv, the tv branch is not reachable *) - | _ -> ctx.local + | _ -> man.local - let body ctx (f:fundec) : D.t = + let body man (f:fundec) : D.t = (* Initialize locals to top *) - List.fold_left (fun m l -> D.add l (I.top ()) m) ctx.local f.slocals + List.fold_left (fun m l -> D.add l (I.top ()) m) man.local f.slocals - let return ctx (exp:exp option) (f:fundec) : D.t = + let return man (exp:exp option) (f:fundec) : D.t = (* Do nothing, as we are not interested in return values for now. *) - ctx.local + man.local - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = (* Set the formal int arguments to top *) let callee_state = List.fold_left (fun m l -> D.add l (I.top ()) m) (D.bot ()) f.sformals in - [(ctx.local, callee_state)] + [(man.local, callee_state)] let set_local_int_lval_top (state: D.t) (lval: lval option) = match lval with @@ -82,18 +82,18 @@ struct ) |_ -> state - let combine_env ctx lval fexp f args fc au f_ask = - ctx.local (* keep local as opposed to IdentitySpec *) + let combine_env man lval fexp f args fc au f_ask = + man.local (* keep local as opposed to IdentitySpec *) - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask): D.t = + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask): D.t = (* If we have a function call with assignment x = f (e1, ... , ek) with a local int variable x on the left, we set it to top *) - set_local_int_lval_top ctx.local lval + set_local_int_lval_top man.local lval - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = (* When calling a special function, and assign the result to some local int variable, we also set it to top. *) - set_local_int_lval_top ctx.local lval + set_local_int_lval_top man.local lval let startstate v = D.bot () let exitstate v = D.top () (* TODO: why is this different from startstate? *) diff --git a/src/analyses/tutorials/signs.ml b/src/analyses/tutorials/signs.ml index 28f7e0acdc..a605bb3910 100644 --- a/src/analyses/tutorials/signs.ml +++ b/src/analyses/tutorials/signs.ml @@ -66,8 +66,8 @@ struct (* Transfer functions: we only implement assignments here. * You can leave this code alone... *) - let assign ctx (lval:lval) (rval:exp) : D.t = - let d = ctx.local in + let assign man (lval:lval) (rval:exp) : D.t = + let d = man.local in match lval with | (Var x, NoOffset) when not x.vaddrof -> D.add x (eval d rval) d | _ -> D.top () @@ -81,10 +81,10 @@ struct (* We should now provide this information to Goblint. Assertions are integer expressions, * so we implement here a response to EvalInt queries. * You should definitely leave this alone... *) - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = let open Queries in match q with - | EvalInt e when assert_holds ctx.local e -> + | EvalInt e when assert_holds man.local e -> let ik = Cilfacade.get_ikind_exp e in ID.of_bool ik true | _ -> Result.top q diff --git a/src/analyses/tutorials/taint.ml b/src/analyses/tutorials/taint.ml index f62a5a4722..6f341618cc 100644 --- a/src/analyses/tutorials/taint.ml +++ b/src/analyses/tutorials/taint.ml @@ -28,7 +28,7 @@ struct module C = Printable.Unit (* We are context insensitive in this analysis *) - let context ctx _ _ = () + let context man _ _ = () let startcontext () = () @@ -58,8 +58,8 @@ struct (* transfer functions *) (** Handles assignment of [rval] to [lval]. *) - let assign ctx (lval:lval) (rval:exp) : D.t = - let state = ctx.local in + let assign man (lval:lval) (rval:exp) : D.t = + let state = man.local in match lval with | Var v,_ -> (* TODO: Check whether rval is tainted, handle assignment to v accordingly *) @@ -67,19 +67,19 @@ struct | _ -> state (** Handles conditional branching yielding truth value [tv]. *) - let branch ctx (exp:exp) (tv:bool) : D.t = + let branch man (exp:exp) (tv:bool) : D.t = (* Nothing needs to be done *) - ctx.local + man.local (** Handles going from start node of function [f] into the function body of [f]. Meant to handle e.g. initializiation of local variables. *) - let body ctx (f:fundec) : D.t = + let body man (f:fundec) : D.t = (* Nothing needs to be done here, as the (non-formals) locals are initally untainted *) - ctx.local + man.local (** Handles the [return] statement, i.e. "return exp" or "return", in function [f]. *) - let return ctx (exp:exp option) (f:fundec) : D.t = - let state = ctx.local in + let return man (exp:exp option) (f:fundec) : D.t = + let state = man.local in match exp with | Some e -> (* TODO: Record whether a tainted value was returned. *) @@ -91,8 +91,8 @@ struct [enter] returns a caller state, and the initial state of the callee. In [enter], the caller state can usually be returned unchanged, as [combine_env] and [combine_assign] (below) will compute the caller state after the function call, given the return state of the callee. *) - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - let caller_state = ctx.local in + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let caller_state = man.local in (* Create list of (formal, actual_exp)*) let zipped = List.combine f.sformals args in (* TODO: For the initial callee_state, collect formal parameters where the actual is tainted. *) @@ -108,31 +108,31 @@ struct (** For a function call "lval = f(args)" or "f(args)", computes the global environment state of the caller after the call. Argument [callee_local] is the state of [f] at its return node. *) - let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = + let combine_env man (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = (* Nothing needs to be done *) - ctx.local + man.local (** For a function call "lval = f(args)" or "f(args)", computes the state of the caller after assigning the return value from the call. Argument [callee_local] is the state of [f] at its return node. *) - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = - let caller_state = ctx.local in + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = + let caller_state = man.local in (* TODO: Record whether lval was tainted. *) caller_state (** For a call to a _special_ function f "lval = f(args)" or "f(args)", computes the caller state after the function call. For this analysis, source and sink functions will be considered _special_ and have to be treated here. *) - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - let caller_state = ctx.local in + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let caller_state = man.local in (* TODO: Check if f is a sink / source and handle it appropriately *) (* To warn about a potential issue in the code, use M.warn. *) caller_state (* You may leave these alone *) let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadenter man ~multiple lval f args = [D.top ()] + let threadspawn man ~multiple lval f args fman = man.local let exitstate v = D.top () end diff --git a/src/analyses/tutorials/unitAnalysis.ml b/src/analyses/tutorials/unitAnalysis.ml index 225767f010..23c336bfa1 100644 --- a/src/analyses/tutorials/unitAnalysis.ml +++ b/src/analyses/tutorials/unitAnalysis.ml @@ -14,34 +14,34 @@ struct module C = Printable.Unit (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - ctx.local + let assign man (lval:lval) (rval:exp) : D.t = + man.local - let branch ctx (exp:exp) (tv:bool) : D.t = - ctx.local + let branch man (exp:exp) (tv:bool) : D.t = + man.local - let body ctx (f:fundec) : D.t = - ctx.local + let body man (f:fundec) : D.t = + man.local - let return ctx (exp:exp option) (f:fundec) : D.t = - ctx.local + let return man (exp:exp option) (f:fundec) : D.t = + man.local - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - [ctx.local, ctx.local] + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + [man.local, man.local] - let combine_env ctx lval fexp f args fc au f_ask = + let combine_env man lval fexp f args fc au f_ask = au - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = - ctx.local + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = + man.local - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - ctx.local + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + man.local let startcontext () = () let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadenter man ~multiple lval f args = [D.top ()] + let threadspawn man ~multiple lval f args fman = man.local let exitstate v = D.top () end diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 1b430e651a..fe1c4809cd 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -248,39 +248,39 @@ struct | Error (`Msg e) -> M.error_noloc ~category:Witness "couldn't parse entry: %s" e ) yaml_entries - let emit_unassume ctx = - let es = NH.find_all invs ctx.node in + let emit_unassume man = + let es = NH.find_all invs man.node in let es = D.fold (fun pre acc -> - match NH.find_option pre_invs ctx.node with + match NH.find_option pre_invs man.node with | Some eh -> EH.find_all eh pre @ acc | None -> acc - ) ctx.local es + ) man.local es 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 !AnalysisState.postsolving then ( - if not (GobConfig.get_bool "ana.unassume.precheck" && Queries.ID.to_bool (ctx.ask (EvalInt e)) = Some false) then ( + if not (GobConfig.get_bool "ana.unassume.precheck" && Queries.ID.to_bool (man.ask (EvalInt e)) = Some false) then ( let tokens = x.token :: List.map (fun {token; _} -> token) xs in - ctx.emit (Unassume {exp = e; tokens}); + man.emit (Unassume {exp = e; tokens}); List.iter WideningTokenLifter.add tokens ) ); - ctx.local + man.local | [] -> - ctx.local + man.local - let assign ctx lv e = - emit_unassume ctx + let assign man lv e = + emit_unassume man - let branch ctx e tv = - emit_unassume ctx + let branch man e tv = + emit_unassume man - let body ctx fd = + let body man fd = let pres = FH.find_all fun_pres fd in let st = List.fold_left (fun acc pre -> - let v = ctx.ask (EvalInt pre) in + let v = man.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 @@ -289,25 +289,25 @@ struct ) (D.empty ()) pres in - emit_unassume {ctx with local = st} (* doesn't query, so no need to redefine ask *) + emit_unassume {man with local = st} (* doesn't query, so no need to redefine ask *) - let asm ctx = - emit_unassume ctx + let asm man = + emit_unassume man - let skip ctx = - emit_unassume ctx + let skip man = + emit_unassume man - let special ctx lv f args = - emit_unassume ctx + let special man lv f args = + emit_unassume man - let enter ctx lv f args = - [(ctx.local, D.empty ())] + let enter man lv f args = + [(man.local, D.empty ())] - let combine_env ctx lval fexp f args fc au f_ask = - ctx.local (* not here because isn't final transfer function on edge *) + let combine_env man lval fexp f args fc au f_ask = + man.local (* not here because isn't final transfer function on edge *) - let combine_assign ctx lv fe f args fc fd f_ask = - emit_unassume ctx + let combine_assign man lv fe f args fc fd f_ask = + emit_unassume man (* 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? *) diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index 8c217cda4e..a8689d9e8b 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -25,8 +25,8 @@ struct let name () = "uninit" let startstate v : D.t = D.empty () - let threadenter ctx ~multiple lval f args = [D.empty ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadenter man ~multiple lval f args = [D.empty ()] + let threadspawn man ~multiple lval f args fman = man.local let exitstate v : D.t = D.empty () let access_address (ask: Queries.ask) write lv = @@ -219,48 +219,48 @@ struct (* Transfer functions *) - let assign ctx (lval:lval) (rval:exp) : trans_out = - ignore (is_expr_initd (Analyses.ask_of_ctx ctx) rval ctx.local); - init_lval (Analyses.ask_of_ctx ctx) lval ctx.local + let assign man (lval:lval) (rval:exp) : trans_out = + ignore (is_expr_initd (Analyses.ask_of_man man) rval man.local); + init_lval (Analyses.ask_of_man man) lval man.local - let branch ctx (exp:exp) (tv:bool) : trans_out = - ignore (is_expr_initd (Analyses.ask_of_ctx ctx) exp ctx.local); - ctx.local + let branch man (exp:exp) (tv:bool) : trans_out = + ignore (is_expr_initd (Analyses.ask_of_man man) exp man.local); + man.local - let body ctx (f:fundec) : trans_out = + let body man (f:fundec) : trans_out = let add_var st v = List.fold_right D.add (to_addrs v) st in - List.fold_left add_var ctx.local f.slocals + List.fold_left add_var man.local f.slocals - let return ctx (exp:exp option) (f:fundec) : trans_out = + let return man (exp:exp option) (f:fundec) : trans_out = let remove_var x v = List.fold_right D.remove (to_addrs v) x in - let nst = List.fold_left remove_var ctx.local (f.slocals @ f.sformals) in + let nst = List.fold_left remove_var man.local (f.slocals @ f.sformals) in match exp with - | Some exp -> ignore (is_expr_initd (Analyses.ask_of_ctx ctx) exp ctx.local); nst + | Some exp -> ignore (is_expr_initd (Analyses.ask_of_man man) exp man.local); nst | _ -> nst - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - let nst = remove_unreachable (Analyses.ask_of_ctx ctx) args ctx.local in - [ctx.local, nst] + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let nst = remove_unreachable (Analyses.ask_of_man man) args man.local in + [man.local, nst] - let combine_env ctx lval fexp f args fc au f_ask = - ignore (List.map (fun x -> is_expr_initd (Analyses.ask_of_ctx ctx) x ctx.local) args); - let cal_st = remove_unreachable (Analyses.ask_of_ctx ctx) args ctx.local in - D.union au (D.diff ctx.local cal_st) + let combine_env man lval fexp f args fc au f_ask = + ignore (List.map (fun x -> is_expr_initd (Analyses.ask_of_man man) x man.local) args); + let cal_st = remove_unreachable (Analyses.ask_of_man man) args man.local in + D.union au (D.diff man.local cal_st) - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : trans_out = + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : trans_out = match lval with - | None -> ctx.local - | Some lv -> init_lval (Analyses.ask_of_ctx ctx) lv ctx.local + | None -> man.local + | Some lv -> init_lval (Analyses.ask_of_man man) lv man.local - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = match lval with - | Some lv -> init_lval (Analyses.ask_of_ctx ctx) lv ctx.local - | _ -> ctx.local + | Some lv -> init_lval (Analyses.ask_of_man man) lv man.local + | _ -> man.local - (* let fork ctx (lval: lval option) (f : varinfo) (args : exp list) : (varinfo * D.t) list = + (* let fork man (lval: lval option) (f : varinfo) (args : exp list) : (varinfo * D.t) list = [] (* thats wrong: should be [None, top ()] *)*) end diff --git a/src/analyses/useAfterFree.ml b/src/analyses/useAfterFree.ml index 6aa3e1e84c..32a095a13c 100644 --- a/src/analyses/useAfterFree.ml +++ b/src/analyses/useAfterFree.ml @@ -25,16 +25,16 @@ struct (* HELPER FUNCTIONS *) - let get_current_threadid ctx = - ctx.ask Queries.CurrentThreadId + let get_current_threadid man = + man.ask Queries.CurrentThreadId - let get_joined_threads ctx = - ctx.ask Queries.MustJoinedThreads + let get_joined_threads man = + man.ask Queries.MustJoinedThreads - let warn_for_multi_threaded_access ctx ?(is_double_free = false) (heap_var:varinfo) behavior cwe_number = - let freeing_threads = ctx.global heap_var in + let warn_for_multi_threaded_access man ?(is_double_free = false) (heap_var:varinfo) behavior cwe_number = + let freeing_threads = man.global heap_var in (* If we're single-threaded or there are no threads freeing the memory, we have nothing to WARN about *) - if ctx.ask (Queries.MustBeSingleThreaded { since_start = true }) || G.is_empty freeing_threads then () + if man.ask (Queries.MustBeSingleThreaded { since_start = true }) || G.is_empty freeing_threads then () else begin let other_possibly_started current tid joined_threads = match tid with @@ -42,7 +42,7 @@ struct (* if our own (unique) thread is started here, that is not a problem *) false | `Lifted tid -> - let created_threads = ctx.ask Queries.CreatedThreads in + let created_threads = man.ask Queries.CreatedThreads in let not_started = MHP.definitely_not_started (current, created_threads) tid in let possibly_started = not not_started in (* If [current] is possibly running together with [tid], but is also joined before the free() in [tid], then no need to WARN *) @@ -59,7 +59,7 @@ struct | `Bot -> false in let bug_name = if is_double_free then "Double Free" else "Use After Free" in - match get_current_threadid ctx with + match get_current_threadid man with | `Lifted current -> let possibly_started = G.exists (other_possibly_started current) freeing_threads in if possibly_started then begin @@ -73,7 +73,7 @@ struct if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "Current thread is not unique and a %s might occur for heap variable %a" bug_name CilType.Varinfo.pretty heap_var end - else if HeapVars.mem heap_var (snd ctx.local) then begin + else if HeapVars.mem heap_var (snd man.local) then begin if is_double_free then set_mem_safety_flag InvalidFree else set_mem_safety_flag InvalidDeref; M.warn ~category:(Behavior behavior) ~tags:[CWE cwe_number] "%s might occur in current unique thread %a for heap variable %a" bug_name ThreadIdDomain.Thread.pretty current CilType.Varinfo.pretty heap_var end @@ -85,19 +85,19 @@ struct M.warn ~category:MessageCategory.Analyzer "CurrentThreadId is bottom" end - let rec warn_lval_might_contain_freed ?(is_implicitly_derefed = false) ?(is_double_free = false) (transfer_fn_name:string) ctx (lval:lval) = + let rec warn_lval_might_contain_freed ?(is_implicitly_derefed = false) ?(is_double_free = false) (transfer_fn_name:string) man (lval:lval) = match is_implicitly_derefed, is_double_free, lval with (* If we're not checking for a double-free and there's no deref happening, then there's no need to check for an invalid deref or an invalid free *) | false, false, (Var _, NoOffset) -> () | _ -> - let state = ctx.local in + let state = man.local in let undefined_behavior = if is_double_free then Undefined DoubleFree else Undefined UseAfterFree in let cwe_number = if is_double_free then 415 else 416 in let rec offset_might_contain_freed offset = match offset with | NoOffset -> () | Field (f, o) -> offset_might_contain_freed o - | Index (e, o) -> warn_exp_might_contain_freed transfer_fn_name ctx e; offset_might_contain_freed o + | Index (e, o) -> warn_exp_might_contain_freed transfer_fn_name man e; offset_might_contain_freed o in let (lval_host, o) = lval in offset_might_contain_freed o; (* Check the lval's offset *) let lval_to_query = @@ -105,7 +105,7 @@ struct | Var _ -> Lval lval | Mem _ -> mkAddrOf lval (* Take the lval's address if its lhost is of the form *p, where p is a ptr *) in - begin match ctx.ask (Queries.MayPointTo lval_to_query) with + begin match man.ask (Queries.MayPointTo lval_to_query) with | ad when not (Queries.AD.is_top ad) -> let warn_for_heap_var v = if HeapVars.mem v (snd state) then begin @@ -116,18 +116,18 @@ struct let pointed_to_heap_vars = Queries.AD.fold (fun addr vars -> match addr with - | Queries.AD.Addr.Addr (v,_) when ctx.ask (Queries.IsAllocVar v) -> v :: vars + | Queries.AD.Addr.Addr (v,_) when man.ask (Queries.IsAllocVar v) -> v :: vars | _ -> vars ) ad [] in (* Warn for all heap vars that the lval possibly points to *) List.iter warn_for_heap_var pointed_to_heap_vars; (* Warn for a potential multi-threaded UAF for all heap vars that the lval possibly points to *) - List.iter (fun heap_var -> warn_for_multi_threaded_access ctx ~is_double_free heap_var undefined_behavior cwe_number) pointed_to_heap_vars + List.iter (fun heap_var -> warn_for_multi_threaded_access man ~is_double_free heap_var undefined_behavior cwe_number) pointed_to_heap_vars | _ -> () end - and warn_exp_might_contain_freed ?(is_implicitly_derefed = false) ?(is_double_free = false) (transfer_fn_name:string) ctx (exp:exp) = + and warn_exp_might_contain_freed ?(is_implicitly_derefed = false) ?(is_double_free = false) (transfer_fn_name:string) man (exp:exp) = match exp with (* Base recursion cases *) | Const _ @@ -141,53 +141,53 @@ struct | SizeOfE e | AlignOfE e | UnOp (_, e, _) - | CastE (_, e) -> warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e + | CastE (_, e) -> warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e | BinOp (_, e1, e2, _) -> - warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e1; - warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e2 + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e1; + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e2 | Question (e1, e2, e3, _) -> - warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e1; - warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e2; - warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx e3 + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e1; + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e2; + warn_exp_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man e3 (* Lval cases (need [warn_lval_might_contain_freed] for them) *) | Lval lval | StartOf lval - | AddrOf lval -> warn_lval_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name ctx lval + | AddrOf lval -> warn_lval_might_contain_freed ~is_implicitly_derefed ~is_double_free transfer_fn_name man lval - let side_effect_mem_free ctx freed_heap_vars threadid joined_threads = + let side_effect_mem_free man freed_heap_vars threadid joined_threads = let side_effect_globals_to_heap_var heap_var = - let current_globals = ctx.global heap_var in + let current_globals = man.global heap_var in let globals_to_side_effect = G.add threadid joined_threads current_globals in - ctx.sideg heap_var globals_to_side_effect + man.sideg heap_var globals_to_side_effect in HeapVars.iter side_effect_globals_to_heap_var freed_heap_vars (* TRANSFER FUNCTIONS *) - let assign ctx (lval:lval) (rval:exp) : D.t = - warn_lval_might_contain_freed "assign" ctx lval; - warn_exp_might_contain_freed "assign" ctx rval; - ctx.local + let assign man (lval:lval) (rval:exp) : D.t = + warn_lval_might_contain_freed "assign" man lval; + warn_exp_might_contain_freed "assign" man rval; + man.local - let branch ctx (exp:exp) (tv:bool) : D.t = - warn_exp_might_contain_freed "branch" ctx exp; - ctx.local + let branch man (exp:exp) (tv:bool) : D.t = + warn_exp_might_contain_freed "branch" man exp; + man.local - let return ctx (exp:exp option) (f:fundec) : D.t = - Option.iter (fun x -> warn_exp_might_contain_freed "return" ctx x) exp; - ctx.local + let return man (exp:exp option) (f:fundec) : D.t = + Option.iter (fun x -> warn_exp_might_contain_freed "return" man x) exp; + man.local - let enter ctx (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - let caller_state = ctx.local in - List.iter (fun arg -> warn_exp_might_contain_freed "enter" ctx arg) args; + let enter man (lval:lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let caller_state = man.local in + List.iter (fun arg -> warn_exp_might_contain_freed "enter" man arg) args; (* TODO: The 2nd component of the callee state needs to contain only the heap vars from the caller state which are reachable from: *) (* * Global program variables *) (* * The callee arguments *) [caller_state, (AllocaVars.empty (), snd caller_state)] - let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = - let (caller_stack_state, caller_heap_state) = ctx.local in + let combine_env man (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask:Queries.ask) : D.t = + let (caller_stack_state, caller_heap_state) = man.local in let callee_stack_state = fst callee_local in let callee_heap_state = snd callee_local in (* Put all alloca()-vars together with all freed() vars in the caller's second component *) @@ -195,12 +195,12 @@ struct let callee_combined_state = HeapVars.join callee_stack_state callee_heap_state in (caller_stack_state, HeapVars.join caller_heap_state callee_combined_state) - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = - Option.iter (fun x -> warn_lval_might_contain_freed "enter" ctx x) lval; - ctx.local + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (callee_local:D.t) (f_ask: Queries.ask): D.t = + Option.iter (fun x -> warn_lval_might_contain_freed "enter" man x) lval; + man.local - let special ctx (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = - let state = ctx.local in + let special man (lval:lval option) (f:varinfo) (arglist:exp list) : D.t = + let state = man.local in let desc = LibraryFunctions.find f in let is_arg_implicitly_derefed arg = let read_shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Read; deep = false } arglist in @@ -209,28 +209,28 @@ struct let write_deep_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } arglist in List.mem arg read_shallow_args || List.mem arg read_deep_args || List.mem arg write_shallow_args || List.mem arg write_deep_args in - Option.iter (fun x -> warn_lval_might_contain_freed ("special: " ^ f.vname) ctx x) lval; - List.iter (fun arg -> warn_exp_might_contain_freed ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) ~is_double_free:(match desc.special arglist with Free _ -> true | _ -> false) ("special: " ^ f.vname) ctx arg) arglist; + Option.iter (fun x -> warn_lval_might_contain_freed ("special: " ^ f.vname) man x) lval; + List.iter (fun arg -> warn_exp_might_contain_freed ~is_implicitly_derefed:(is_arg_implicitly_derefed arg) ~is_double_free:(match desc.special arglist with Free _ -> true | _ -> false) ("special: " ^ f.vname) man arg) arglist; match desc.special arglist with | Free ptr -> - begin match ctx.ask (Queries.MayPointTo ptr) with + begin match man.ask (Queries.MayPointTo ptr) with | ad when not (Queries.AD.is_top ad) -> let pointed_to_heap_vars = Queries.AD.fold (fun addr state -> match addr with - | Queries.AD.Addr.Addr (var,_) when ctx.ask (Queries.IsAllocVar var) && ctx.ask (Queries.IsHeapVar var) -> HeapVars.add var state + | Queries.AD.Addr.Addr (var,_) when man.ask (Queries.IsAllocVar var) && man.ask (Queries.IsHeapVar var) -> HeapVars.add var state | _ -> state ) ad (HeapVars.empty ()) in (* Side-effect the tid that's freeing all the heap vars collected here *) - side_effect_mem_free ctx pointed_to_heap_vars (get_current_threadid ctx) (get_joined_threads ctx); + side_effect_mem_free man pointed_to_heap_vars (get_current_threadid man) (get_joined_threads man); (* Add all heap vars, which ptr points to, to the state *) (fst state, HeapVars.join (snd state) pointed_to_heap_vars) | _ -> state end | Alloca _ -> (* Create fresh heap var for the alloca() call *) - begin match ctx.ask (Queries.AllocVar {on_stack = true}) with + begin match man.ask (Queries.AllocVar {on_stack = true}) with | `Lifted v -> (AllocaVars.add v (fst state), snd state) | _ -> state end diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index b220afc0d9..20d09f38d4 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -41,8 +41,8 @@ struct let name () = "var_eq" let startstate v = D.top () - let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadenter man ~multiple lval f args = [D.top ()] + let threadspawn man ~multiple lval f args fman = man.local let exitstate v = D.top () let typ_equal = CilType.Typ.equal (* TODO: Used to have equality checking, which ignores attributes. Is that needed? *) @@ -395,47 +395,47 @@ struct (* Probably ok as is. *) - let body ctx f = ctx.local + let body man f = man.local (* Branch could be improved to set invariants like base tries to do. *) - let branch ctx exp tv = ctx.local + let branch man exp tv = man.local (* Just remove things that go out of scope. *) - let return ctx exp fundec = - let rm v = remove (Analyses.ask_of_ctx ctx) (Var v,NoOffset) in - List.fold_right rm (fundec.sformals@fundec.slocals) ctx.local + let return man exp fundec = + let rm v = remove (Analyses.ask_of_man man) (Var v,NoOffset) in + List.fold_right rm (fundec.sformals@fundec.slocals) man.local (* removes all equalities with lval and then tries to make a new one: lval=rval *) - let assign ctx (lval:lval) (rval:exp) : D.t = + let assign man (lval:lval) (rval:exp) : D.t = let rval = constFold true (stripCasts rval) in - add_eq (Analyses.ask_of_ctx ctx) lval rval ctx.local + add_eq (Analyses.ask_of_man man) lval rval man.local (* First assign arguments to parameters. Then join it with reachables, to get rid of equalities that are not reachable. *) - let enter ctx lval f args = + let enter man lval f args = let rec fold_left2 f r xs ys = match xs, ys with | x::xs, y::ys -> fold_left2 f (f r x y) xs ys | _ -> r in let assign_one_param st lv exp = - let rm = remove (Analyses.ask_of_ctx ctx) (Var lv, NoOffset) st in - add_eq (Analyses.ask_of_ctx ctx) (Var lv, NoOffset) exp rm + let rm = remove (Analyses.ask_of_man man) (Var lv, NoOffset) st in + add_eq (Analyses.ask_of_man man) (Var lv, NoOffset) exp rm in let nst = - try fold_left2 assign_one_param ctx.local f.sformals args + try fold_left2 assign_one_param man.local f.sformals args with SetDomain.Unsupported _ -> (* ignore varargs fr now *) D.top () in - match D.is_bot ctx.local with + match D.is_bot man.local with | true -> raise Analyses.Deadcode - | false -> [ctx.local,nst] + | false -> [man.local,nst] - let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) = + let combine_env man lval fexp f args fc au (f_ask: Queries.ask) = let tainted = f_ask.f Queries.MayBeTainted in let d_local = (* if we are multithreaded, we run the risk, that some mutex protected variables got unlocked, so in this case caller state goes to top TODO: !!Unsound, this analysis does not handle this case -> regtest 63 08!! *) - if Queries.AD.is_top tainted || not (ctx.ask (Queries.MustBeSingleThreaded {since_start = true})) then + if Queries.AD.is_top tainted || not (man.ask (Queries.MustBeSingleThreaded {since_start = true})) then D.top () else let taint_exp = @@ -443,17 +443,17 @@ struct |> List.map Addr.Mval.to_cil_exp |> Queries.ES.of_list in - D.filter (fun exp -> not (Queries.ES.mem exp taint_exp)) ctx.local + D.filter (fun exp -> not (Queries.ES.mem exp taint_exp)) man.local in let d = D.meet au d_local in - match D.is_bot ctx.local with + match D.is_bot man.local with | true -> raise Analyses.Deadcode | false -> d - let combine_assign ctx lval fexp f args fc st2 (f_ask : Queries.ask) = + let combine_assign man lval fexp f args fc st2 (f_ask : Queries.ask) = match lval with - | Some lval -> remove (Analyses.ask_of_ctx ctx) lval ctx.local - | None -> ctx.local + | Some lval -> remove (Analyses.ask_of_man man) lval man.local + | None -> man.local let remove_reachable ~deep ask es st = let rs = reachables ~deep ask es in @@ -468,7 +468,7 @@ struct | _ -> st ) rs st - let unknown_fn ctx lval f args = + let unknown_fn man lval f args = let desc = LF.find f in let shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in let deep_args = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } args in @@ -477,29 +477,29 @@ struct | Some l -> mkAddrOf l :: shallow_args | None -> shallow_args in - match D.is_bot ctx.local with + match D.is_bot man.local with | true -> raise Analyses.Deadcode | false -> - let ask = Analyses.ask_of_ctx ctx in - ctx.local + let ask = Analyses.ask_of_man man in + man.local |> remove_reachable ~deep:false ask shallow_args |> remove_reachable ~deep:true ask deep_args (* remove all variables that are reachable from arguments *) - let special ctx lval f args = + let special man lval f args = let desc = LibraryFunctions.find f in match desc.special args with | Identity e -> begin match lval with - | Some x -> assign ctx x e - | None -> unknown_fn ctx lval f args + | Some x -> assign man x e + | None -> unknown_fn man lval f args end | ThreadCreate { arg; _ } -> - begin match D.is_bot ctx.local with + begin match D.is_bot man.local with | true -> raise Analyses.Deadcode - | false -> remove_reachable ~deep:true (Analyses.ask_of_ctx ctx) [arg] ctx.local + | false -> remove_reachable ~deep:true (Analyses.ask_of_man man) [arg] man.local end - | _ -> unknown_fn ctx lval f args + | _ -> unknown_fn man lval f args (* query stuff *) let eq_set (e:exp) s = @@ -554,20 +554,20 @@ struct r - let query ctx (type a) (x: a Queries.t): a Queries.result = + let query man (type a) (x: a Queries.t): a Queries.result = match x with - | Queries.EvalInt (BinOp (Eq, e1, e2, t)) when query_exp_equal (Analyses.ask_of_ctx ctx) e1 e2 ctx.global ctx.local -> + | Queries.EvalInt (BinOp (Eq, e1, e2, t)) when query_exp_equal (Analyses.ask_of_man man) e1 e2 man.global man.local -> Queries.ID.of_bool (Cilfacade.get_ikind t) true | Queries.EqualSet e -> - let r = eq_set_clos e ctx.local in + let r = eq_set_clos e man.local in if M.tracing then M.tracel "var_eq" "equalset %a = %a" d_plainexp e Queries.ES.pretty r; r | Queries.Invariant context when GobConfig.get_bool "ana.var_eq.invariant.enabled" && GobConfig.get_bool "witness.invariant.exact" -> (* only exact equalities here *) - let scope = Node.find_fundec ctx.node in - D.invariant ~scope ctx.local + let scope = Node.find_fundec man.node in + D.invariant ~scope man.local | _ -> Queries.Result.top x - let event ctx e octx = + let event man e oman = match e with | Events.Unassume {exp; _} -> (* Unassume must forget equalities, @@ -576,19 +576,19 @@ struct 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 + remove (Analyses.ask_of_man man) lv st + ) man.local | Events.Escape vars -> if EscapeDomain.EscapedVars.is_top vars then D.top () else - let ask = Analyses.ask_of_ctx ctx in + let ask = Analyses.ask_of_man man in let remove_var st v = remove ask (Cil.var v) st in - List.fold_left remove_var ctx.local (EscapeDomain.EscapedVars.elements vars) + List.fold_left remove_var man.local (EscapeDomain.EscapedVars.elements vars) | _ -> - ctx.local + man.local end let _ = diff --git a/src/analyses/vla.ml b/src/analyses/vla.ml index aca4fdead8..28a485f5d4 100644 --- a/src/analyses/vla.ml +++ b/src/analyses/vla.ml @@ -10,27 +10,27 @@ struct let name () = "vla" module D = BoolDomain.MayBool - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - [ctx.local, false] + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + [man.local, false] - let combine_env ctx lval fexp f args fc au f_ask = - ctx.local (* keep local as opposed to IdentitySpec *) + let combine_env man lval fexp f args fc au f_ask = + man.local (* keep local as opposed to IdentitySpec *) - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = match (LibraryFunctions.find f).special arglist with | Setjmp _ -> (* Checking if this within the scope of an identifier of variably modified type *) - if ctx.local then + if man.local then M.warn ~category:(Behavior (Undefined Other)) "setjmp called within the scope of a variably modified type. If a call to longjmp is made after this scope is left, the behavior is undefined."; - ctx.local + man.local | _ -> - ctx.local + man.local - let vdecl ctx (v:varinfo) : D.t = - ctx.local || Cilfacade.isVLAType v.vtype + let vdecl man (v:varinfo) : D.t = + man.local || Cilfacade.isVLAType v.vtype let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = [D.top ()] + let threadenter man ~multiple lval f args = [D.top ()] let exitstate v = D.top () end diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index 32ca234e70..b450452a62 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -26,12 +26,12 @@ module SpecBase (UniqueCount : Lattice.S with type t = int) (WrapperArgs : Wrapp struct include IdentitySpec - (* Use the previous CFG node (ctx.prev_node) for identifying calls to (wrapper) functions. + (* Use the previous CFG node (man.prev_node) for identifying calls to (wrapper) functions. For one, this is the node that typically contains the call as its statement. - Additionally, it distinguishes two calls that share the next CFG node (ctx.node), e.g.: + Additionally, it distinguishes two calls that share the next CFG node (man.node), e.g.: if (cond) { x = malloc(1); } else { x = malloc(2); } Introduce a function for this to keep things consistent. *) - let node_for_ctx ctx = ctx.prev_node + let node_for_man man = man.prev_node module NodeFlatLattice = struct @@ -63,40 +63,40 @@ struct (* transfer functions *) - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - let wrapper_node, counter = ctx.local in + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + let wrapper_node, counter = man.local in let new_wrapper_node = if Hashtbl.mem wrappers f.svar.vname then match wrapper_node with (* if an interesting callee is called by an interesting caller, then we remember the caller context *) | `Lifted _ -> wrapper_node (* if an interesting callee is called by an uninteresting caller, then we remember the callee context *) - | _ -> `Lifted (node_for_ctx ctx) + | _ -> `Lifted (node_for_man man) else NodeFlatLattice.top () (* if an uninteresting callee is called, then we forget what was called before *) in let callee = (new_wrapper_node, counter) in - [(ctx.local, callee)] + [(man.local, callee)] - let combine_env ctx lval fexp f args fc (_, counter) f_ask = + let combine_env man lval fexp f args fc (_, counter) f_ask = (* Keep (potentially higher) counter from callee and keep wrapper node from caller *) - let lnode, _ = ctx.local in + let lnode, _ = man.local in (lnode, counter) - let add_unique_call_ctx ctx = - let wrapper_node, counter = ctx.local in + let add_unique_call_man man = + let wrapper_node, counter = man.local in wrapper_node, (* track the unique ID per call to the wrapper function, not to the wrapped function *) add_unique_call counter - (match wrapper_node with `Lifted node -> node | _ -> node_for_ctx ctx) + (match wrapper_node with `Lifted node -> node | _ -> node_for_man man) - let special (ctx: (D.t, G.t, C.t, V.t) ctx) (lval: lval option) (f: varinfo) (arglist:exp list) : D.t = + let special (man: (D.t, G.t, C.t, V.t) man) (lval: lval option) (f: varinfo) (arglist:exp list) : D.t = let desc = LibraryFunctions.find f in - if WrapperArgs.is_wrapped @@ desc.special arglist then add_unique_call_ctx ctx else ctx.local + if WrapperArgs.is_wrapped @@ desc.special arglist then add_unique_call_man man else man.local let startstate v = D.bot () - let threadenter ctx ~multiple lval f args = + let threadenter man ~multiple lval f args = (* The new thread receives a fresh counter *) [D.bot ()] @@ -152,16 +152,16 @@ module MallocWrapper : MCPSpec = struct let name () = "mallocWrapper" - let query (ctx: (D.t, G.t, C.t, V.t) ctx) (type a) (q: a Q.t): a Q.result = - let wrapper_node, counter = ctx.local in + let query (man: (D.t, G.t, C.t, V.t) man) (type a) (q: a Q.t): a Q.result = + let wrapper_node, counter = man.local in match q with | Q.AllocVar {on_stack = on_stack} -> let node = match wrapper_node with | `Lifted wrapper_node -> wrapper_node - | _ -> node_for_ctx ctx + | _ -> node_for_man man in let count = UniqueCallCounter.find (`Lifted node) counter in - let var = NodeVarinfoMap.to_varinfo (ctx.ask Q.CurrentThreadId, node, count) in + let var = NodeVarinfoMap.to_varinfo (man.ask Q.CurrentThreadId, node, count) in var.vdecl <- UpdateCil.getLoc node; (* TODO: does this do anything bad for incremental? *) if on_stack then var.vattr <- addAttribute (Attr ("stack_alloca", [])) var.vattr; (* If the call was for stack allocation, add an attr to mark the heap var *) `Lifted var @@ -171,7 +171,7 @@ module MallocWrapper : MCPSpec = struct NodeVarinfoMap.mem_varinfo v | Q.IsMultiple v -> begin match NodeVarinfoMap.from_varinfo v with - | Some (_, _, c) -> UniqueCount.is_top c || not (ctx.ask Q.MustBeUniqueThread) + | Some (_, _, c) -> UniqueCount.is_top c || not (man.ask Q.MustBeUniqueThread) | None -> false end | _ -> Queries.Result.top q @@ -203,13 +203,13 @@ module ThreadCreateWrapper : MCPSpec = struct let name () = "threadCreateWrapper" - let query (ctx: (D.t, G.t, C.t, V.t) ctx) (type a) (q: a Q.t): a Q.result = + let query (man: (D.t, G.t, C.t, V.t) man) (type a) (q: a Q.t): a Q.result = match q with | Q.ThreadCreateIndexedNode -> - let wrapper_node, counter = ctx.local in + let wrapper_node, counter = man.local in let node = match wrapper_node with | `Lifted wrapper_node -> wrapper_node - | _ -> node_for_ctx ctx + | _ -> node_for_man man in let count = UniqueCallCounter.find (`Lifted node) counter in `Lifted node, count diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index b9d93bfd99..112e327530 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -143,7 +143,7 @@ struct might be able to be represented by means of 2 var equalities This simplification happens during a time, when there are temporary variables a#in and a#out part of the expression, - but are not represented in the ctx, thus queries may result in top for these variables. Wrapping this in speculative + but are not represented in the man, thus queries may result in top for these variables. Wrapping this in speculative mode is a stop-gap measure to avoid flagging overflows. We however should address simplification in a more generally useful way. outside of the apron-related expression conversion. *) diff --git a/src/domains/events.ml b/src/domains/events.ml index cf12900c98..cc4af83819 100644 --- a/src/domains/events.ml +++ b/src/domains/events.ml @@ -11,7 +11,7 @@ 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; ad: Queries.AD.t; kind: AccessKind.t; reach: bool} - | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [ctx.assign]. *) (* TODO: unused *) + | Assign of {lval: CilType.Lval.t; exp: CilType.Exp.t} (** Used to simulate old [man.assign]. *) (* TODO: unused *) | UpdateExpSplit of exp (** Used by expsplit analysis to evaluate [exp] on post-state. *) | Assert of exp | Unassume of {exp: CilType.Exp.t; tokens: WideningToken.t list} diff --git a/src/domains/queries.ml b/src/domains/queries.ml index fee44a6b24..f43cd77eca 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -137,9 +137,9 @@ type _ t = type 'a result = 'a -(** Container for explicitly polymorphic [ctx.ask] function out of [ctx]. - To be used when passing entire [ctx] around seems inappropriate. - Use [Analyses.ask_of_ctx] to convert [ctx] to [ask]. *) +(** Container for explicitly polymorphic [man.ask] function out of [man]. + To be used when passing entire [man] around seems inappropriate. + Use [Analyses.ask_of_man] to convert [man] to [ask]. *) (* Must be in a singleton record due to second-order polymorphism. See https://ocaml.org/manual/polymorphism.html#s%3Ahigher-rank-poly. *) type ask = { f: 'a. 'a t -> 'a result } [@@unboxed] diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index ab41335944..985f013ede 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -143,23 +143,13 @@ struct end -(* Experiment to reduce the number of arguments on transfer functions and allow - sub-analyses. The list sub contains the current local states of analyses in - the same order as written in the dependencies list (in MCP). - - The foreign states when calling special_fn or enter are joined if the foreign - analysis tries to be path-sensitive in these functions. First try to only - depend on simple analyses. - - It is not clear if we need pre-states, post-states or both on foreign analyses. -*) -type ('d,'g,'c,'v) ctx = +type ('d,'g,'c,'v) man = { ask : 'a. 'a Queries.t -> 'a Queries.result (* Inlined Queries.ask *) ; emit : Events.t -> unit ; node : MyCFG.node ; prev_node: MyCFG.node - ; control_context : unit -> ControlSpecC.t (** top-level Control Spec context, raises [Ctx_failure] if missing *) - ; context : unit -> 'c (** current Spec context, raises [Ctx_failure] if missing *) + ; control_context : unit -> ControlSpecC.t (** top-level Control Spec context, raises [Man_failure] if missing *) + ; context : unit -> 'c (** current Spec context, raises [Man_failure] if missing *) ; edge : MyCFG.edge ; local : 'd ; global : 'v -> 'g @@ -168,13 +158,13 @@ type ('d,'g,'c,'v) ctx = ; sideg : 'v -> 'g -> unit } -exception Ctx_failure of string -(** Failure from ctx, e.g. global initializer *) +exception Man_failure of string +(** Failure from man, e.g. global initializer *) -let ctx_failwith s = raise (Ctx_failure s) (* TODO: use everywhere in ctx *) +let man_failwith s = raise (Man_failure s) (* TODO: use everywhere in man *) -(** Convert [ctx] to [Queries.ask]. *) -let ask_of_ctx ctx: Queries.ask = { Queries.f = ctx.ask } +(** Convert [man] to [Queries.ask]. *) +let ask_of_man man: Queries.ask = { Queries.f = man.ask } module type Spec = @@ -205,48 +195,48 @@ sig val morphstate : varinfo -> D.t -> D.t val exitstate : varinfo -> D.t - val context: (D.t, G.t, C.t, V.t) ctx -> fundec -> D.t -> C.t + val context: (D.t, G.t, C.t, V.t) man -> fundec -> D.t -> C.t val startcontext: unit -> C.t - val sync : (D.t, G.t, C.t, V.t) ctx -> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return] -> D.t - val query : (D.t, G.t, C.t, V.t) ctx -> 'a Queries.t -> 'a Queries.result + val sync : (D.t, G.t, C.t, V.t) man -> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return] -> D.t + val query : (D.t, G.t, C.t, V.t) man -> 'a Queries.t -> 'a Queries.result (** A transfer function which handles the assignment of a rval to a lval, i.e., it handles program points of the form "lval = rval;" *) - val assign: (D.t, G.t, C.t, V.t) ctx -> lval -> exp -> D.t + val assign: (D.t, G.t, C.t, V.t) man -> lval -> exp -> D.t (** A transfer function used for declaring local variables. By default only for variable-length arrays (VLAs). *) - val vdecl : (D.t, G.t, C.t, V.t) ctx -> varinfo -> D.t + val vdecl : (D.t, G.t, C.t, V.t) man -> varinfo -> D.t (** A transfer function which handles conditional branching yielding the truth value passed as a boolean argument *) - val branch: (D.t, G.t, C.t, V.t) ctx -> exp -> bool -> D.t + val branch: (D.t, G.t, C.t, V.t) man -> exp -> bool -> D.t (** A transfer function which handles going from the start node of a function (fundec) into its function body. Meant to handle, e.g., initialization of local variables *) - val body : (D.t, G.t, C.t, V.t) ctx -> fundec -> D.t + val body : (D.t, G.t, C.t, V.t) man -> fundec -> D.t (** A transfer function which handles the return statement, i.e., "return exp" or "return" in the passed function (fundec) *) - val return: (D.t, G.t, C.t, V.t) ctx -> exp option -> fundec -> D.t + val return: (D.t, G.t, C.t, V.t) man -> exp option -> fundec -> D.t (** A transfer function meant to handle inline assembler program points *) - val asm : (D.t, G.t, C.t, V.t) ctx -> D.t + val asm : (D.t, G.t, C.t, V.t) man -> D.t (** A transfer function which works as the identity function, i.e., it skips and does nothing. Used for empty loops. *) - val skip : (D.t, G.t, C.t, V.t) ctx -> D.t + val skip : (D.t, G.t, C.t, V.t) man -> D.t (** A transfer function which, for a call to a {e special} function f "lval = f(args)" or "f(args)", computes the caller state after the function call *) - val special : (D.t, G.t, C.t, V.t) ctx -> lval option -> varinfo -> exp list -> D.t + val special : (D.t, G.t, C.t, V.t) man -> lval option -> varinfo -> exp list -> D.t (** For a function call "lval = f(args)" or "f(args)", [enter] returns a caller state, and the initial state of the callee. In [enter], the caller state can usually be returned unchanged, as [combine_env] and [combine_assign] (below) will compute the caller state after the function call, given the return state of the callee *) - val enter : (D.t, G.t, C.t, V.t) ctx -> lval option -> fundec -> exp list -> (D.t * D.t) list + val enter : (D.t, G.t, C.t, V.t) man -> lval option -> fundec -> exp list -> (D.t * D.t) list (* Combine is split into two steps: *) @@ -254,24 +244,24 @@ sig between local state (first component from enter) and function return. This shouldn't yet assign to the lval. *) - val combine_env : (D.t, G.t, C.t, V.t) ctx -> lval option -> exp -> fundec -> exp list -> C.t option -> D.t -> Queries.ask -> D.t + val combine_env : (D.t, G.t, C.t, V.t) man -> lval option -> exp -> fundec -> exp list -> C.t option -> D.t -> Queries.ask -> D.t (** Combine return value assignment to local state (result from combine_env) and function return. This should only assign to the lval. *) - val combine_assign : (D.t, G.t, C.t, V.t) ctx -> lval option -> exp -> fundec -> exp list -> C.t option -> D.t -> Queries.ask -> D.t + val combine_assign : (D.t, G.t, C.t, V.t) man -> lval option -> exp -> fundec -> exp list -> C.t option -> D.t -> Queries.ask -> D.t (* Paths as sets: I know this is ugly! *) - val paths_as_set : (D.t, G.t, C.t, V.t) ctx -> D.t list + val paths_as_set : (D.t, G.t, C.t, V.t) man -> D.t list (** Returns initial state for created thread. *) - val threadenter : (D.t, G.t, C.t, V.t) ctx -> multiple:bool -> lval option -> varinfo -> exp list -> D.t list + val threadenter : (D.t, G.t, C.t, V.t) man -> multiple:bool -> lval option -> varinfo -> exp list -> D.t list (** Updates the local state of the creator thread using initial state of created thread. *) - val threadspawn : (D.t, G.t, C.t, V.t) ctx -> multiple:bool -> lval option -> varinfo -> exp list -> (D.t, G.t, C.t, V.t) ctx -> D.t + val threadspawn : (D.t, G.t, C.t, V.t) man -> multiple:bool -> lval option -> varinfo -> exp list -> (D.t, G.t, C.t, V.t) man -> D.t - val event : (D.t, G.t, C.t, V.t) ctx -> Events.t -> (D.t, G.t, C.t, V.t) ctx -> D.t + val event : (D.t, G.t, C.t, V.t) man -> Events.t -> (D.t, G.t, C.t, V.t) man -> D.t end module type Spec2Spec = functor (S: Spec) -> Spec @@ -288,7 +278,7 @@ sig include Spec module A: MCPA - val access: (D.t, G.t, C.t, V.t) ctx -> Queries.access -> A.t + val access: (D.t, G.t, C.t, V.t) man -> Queries.access -> A.t end type increment_data = { @@ -357,7 +347,7 @@ struct (* no inits nor finalize -- only analyses like Mutex, Base, ... need these to do postprocessing or other imperative hacks. *) - let vdecl ctx _ = ctx.local + let vdecl man _ = man.local let asm x = M.msg_final Info ~category:Unsound "ASM ignored"; @@ -369,18 +359,18 @@ struct let query _ (type a) (q: a Queries.t) = Queries.Result.top q (* Don't know anything --- most will want to redefine this. *) - let event ctx _ _ = ctx.local + let event man _ _ = man.local let morphstate v d = d (* Only for those who track thread IDs. *) - let sync ctx _ = ctx.local + let sync man _ = man.local (* Most domains do not have a global part. *) - let context ctx fd x = x + let context man fd x = x (* Everything is context sensitive --- override in MCP and maybe elsewhere*) - let paths_as_set ctx = [ctx.local] + let paths_as_set man = [man.local] module A = UnitA let access _ _ = () @@ -390,39 +380,39 @@ end module IdentitySpec = struct include DefaultSpec - let assign ctx (lval:lval) (rval:exp) = - ctx.local + let assign man (lval:lval) (rval:exp) = + man.local - let branch ctx (exp:exp) (tv:bool) = - ctx.local + let branch man (exp:exp) (tv:bool) = + man.local - let body ctx (f:fundec) = - ctx.local + let body man (f:fundec) = + man.local - let return ctx (exp:exp option) (f:fundec) = - ctx.local + let return man (exp:exp option) (f:fundec) = + man.local - let enter ctx (lval: lval option) (f:fundec) (args:exp list) = - [ctx.local, ctx.local] + let enter man (lval: lval option) (f:fundec) (args:exp list) = + [man.local, man.local] - let combine_env ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc au (f_ask: Queries.ask) = + let combine_env man (lval:lval option) fexp (f:fundec) (args:exp list) fc au (f_ask: Queries.ask) = au - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc au (f_ask: Queries.ask) = - ctx.local + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc au (f_ask: Queries.ask) = + man.local - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) = - ctx.local + let special man (lval: lval option) (f:varinfo) (arglist:exp list) = + man.local - let threadenter ctx ~multiple lval f args = [ctx.local] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadenter man ~multiple lval f args = [man.local] + let threadspawn man ~multiple lval f args fman = man.local end module IdentityUnitContextsSpec = struct include IdentitySpec module C = Printable.Unit - let context ctx _ _ = () + let context man _ _ = () let startcontext () = () end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index fb4b5081e8..18a7f5191a 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -38,24 +38,24 @@ struct 1. S.V -> S.G -- used for Spec 2. fundec -> set of S.C -- used for IterSysVars Node *) - let sync ctx = - match ctx.prev_node, Cfg.prev ctx.prev_node with + let sync man = + match man.prev_node, Cfg.prev man.prev_node with | _, _ :: _ :: _ -> (* Join in CFG. *) - S.sync ctx `Join + S.sync man `Join | FunctionEntry f, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) - S.sync ctx (`JoinCall f) - | _, _ -> S.sync ctx `Normal + S.sync man (`JoinCall f) + | _, _ -> S.sync man `Normal let side_context sideg f c = if !AnalysisState.postsolving then sideg (GVar.contexts f) (G.create_contexts (G.CSet.singleton c)) - let common_ctx var edge prev_node pval (getl:lv -> ld) sidel getg sideg : (D.t, S.G.t, S.C.t, S.V.t) ctx * D.t list ref * (lval option * varinfo * exp list * D.t * bool) list ref = + let common_man var edge prev_node pval (getl:lv -> ld) sidel getg sideg : (D.t, S.G.t, S.C.t, S.V.t) man * D.t list ref * (lval option * varinfo * exp list * D.t * bool) list ref = let r = ref [] in let spawns = ref [] in (* now watch this ... *) - let rec ctx = - { ask = (fun (type a) (q: a Queries.t) -> S.query ctx q) + let rec man = + { ask = (fun (type a) (q: a Queries.t) -> S.query man q) ; emit = (fun _ -> failwith "emit outside MCP") ; node = fst var ; prev_node = prev_node @@ -69,14 +69,14 @@ struct ; sideg = (fun g d -> sideg (GVar.spec g) (G.create_spec d)) } and spawn ?(multiple=false) lval f args = - (* TODO: adjust ctx node/edge? *) + (* TODO: adjust man node/edge? *) (* TODO: don't repeat for all paths that spawn same *) - let ds = S.threadenter ~multiple ctx lval f args in + let ds = S.threadenter ~multiple man lval f args in List.iter (fun d -> spawns := (lval, f, args, d, multiple) :: !spawns; match Cilfacade.find_varinfo_fundec f with | fd -> - let c = S.context ctx fd d in + let c = S.context man fd d in sidel (FunctionEntry fd, c) d; ignore (getl (Function fd, c)) | exception Not_found -> @@ -86,142 +86,142 @@ struct ) ds in (* ... nice, right! *) - let pval = sync ctx in - { ctx with local = pval }, r, spawns + let pval = sync man in + { man with local = pval }, r, spawns let rec bigsqcup = function | [] -> D.bot () | [x] -> x | x::xs -> D.join x (bigsqcup xs) - let thread_spawns ctx d spawns = + let thread_spawns man d spawns = if List.is_empty spawns then d else - let rec ctx' = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query ctx' q) + let rec man' = + { man with + ask = (fun (type a) (q: a Queries.t) -> S.query man' q) ; local = d } in (* TODO: don't forget path dependencies *) let one_spawn (lval, f, args, fd, multiple) = - let rec fctx = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query fctx q) + let rec fman = + { man with + ask = (fun (type a) (q: a Queries.t) -> S.query fman q) ; local = fd } in - S.threadspawn ctx' ~multiple lval f args fctx + S.threadspawn man' ~multiple lval f args fman in bigsqcup (List.map one_spawn spawns) - let common_join ctx d splits spawns = - thread_spawns ctx (bigsqcup (d :: splits)) spawns + let common_join man d splits spawns = + thread_spawns man (bigsqcup (d :: splits)) spawns - let common_joins ctx ds splits spawns = common_join ctx (bigsqcup ds) splits spawns + let common_joins man ds splits spawns = common_join man (bigsqcup ds) splits spawns let tf_assign var edge prev_node lv e getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.assign ctx lv e in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns + let man, r, spawns = common_man var edge prev_node d getl sidel getg sideg in + let d = S.assign man lv e in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join man d !r !spawns let tf_vdecl var edge prev_node v getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.vdecl ctx v in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns + let man, r, spawns = common_man var edge prev_node d getl sidel getg sideg in + let d = S.vdecl man v in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join man d !r !spawns - let normal_return r fd ctx sideg = - let spawning_return = S.return ctx r fd in - let nval = S.sync { ctx with local = spawning_return } `Return in + let normal_return r fd man sideg = + let spawning_return = S.return man r fd in + let nval = S.sync { man with local = spawning_return } `Return in nval - let toplevel_kernel_return r fd ctx sideg = - let st = if fd.svar.vname = MyCFG.dummy_func.svar.vname then ctx.local else S.return ctx r fd in - let spawning_return = S.return {ctx with local = st} None MyCFG.dummy_func in - let nval = S.sync { ctx with local = spawning_return } `Return in + let toplevel_kernel_return r fd man sideg = + let st = if fd.svar.vname = MyCFG.dummy_func.svar.vname then man.local else S.return man r fd in + let spawning_return = S.return {man with local = st} None MyCFG.dummy_func in + let nval = S.sync { man with local = spawning_return } `Return in nval let tf_ret var edge prev_node ret fd getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in + let man, r, spawns = common_man var edge prev_node d getl sidel getg sideg in let d = (* Force transfer function to be evaluated before dereferencing in common_join argument. *) if (CilType.Fundec.equal fd MyCFG.dummy_func || List.mem fd.svar.vname (get_string_list "mainfun")) && get_bool "kernel" - then toplevel_kernel_return ret fd ctx sideg - else normal_return ret fd ctx sideg + then toplevel_kernel_return ret fd man sideg + else normal_return ret fd man sideg in - common_join ctx d !r !spawns + common_join man d !r !spawns let tf_entry var edge prev_node fd getl sidel getg sideg d = (* Side effect function context here instead of at sidel to FunctionEntry, because otherwise context for main functions (entrystates) will be missing or pruned during postsolving. *) let c: unit -> S.C.t = snd var |> Obj.obj in side_context sideg fd (c ()); - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.body ctx fd in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns + let man, r, spawns = common_man var edge prev_node d getl sidel getg sideg in + let d = S.body man fd in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join man d !r !spawns let tf_test var edge prev_node e tv getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.branch ctx e tv in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns + let man, r, spawns = common_man var edge prev_node d getl sidel getg sideg in + let d = S.branch man e tv in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join man d !r !spawns - let tf_normal_call ctx lv e (f:fundec) args getl sidel getg sideg = + let tf_normal_call man lv e (f:fundec) args getl sidel getg sideg = let combine (cd, fc, fd) = if M.tracing then M.traceli "combine" "local: %a" S.D.pretty cd; if M.tracing then M.trace "combine" "function: %a" S.D.pretty fd; - let rec cd_ctx = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); + let rec cd_man = + { man with + ask = (fun (type a) (q: a Queries.t) -> S.query cd_man q); local = cd; } in - let fd_ctx = - (* Inner scope to prevent unsynced fd_ctx from being used. *) + let fd_man = + (* Inner scope to prevent unsynced fd_man from being used. *) (* Extra sync in case function has multiple returns. Each `Return sync is done before joining, so joined value may be unsound. - Since sync is normally done before tf (in common_ctx), simulate it here for fd. *) + Since sync is normally done before tf (in common_man), simulate it here for fd. *) (* TODO: don't do this extra sync here *) - let rec sync_ctx = - { ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query sync_ctx q); + let rec sync_man = + { man with + ask = (fun (type a) (q: a Queries.t) -> S.query sync_man q); local = fd; prev_node = Function f; } in - (* TODO: more accurate ctx? *) - let synced = sync sync_ctx in - let rec fd_ctx = - { sync_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query fd_ctx q); + (* TODO: more accurate man? *) + let synced = sync sync_man in + let rec fd_man = + { sync_man with + ask = (fun (type a) (q: a Queries.t) -> S.query fd_man q); local = synced; } in - fd_ctx + fd_man in let r = List.fold_left (fun acc fd1 -> - let rec fd1_ctx = - { fd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query fd1_ctx q); + let rec fd1_man = + { fd_man with + ask = (fun (type a) (q: a Queries.t) -> S.query fd1_man q); local = fd1; } in - let combine_enved = S.combine_env cd_ctx lv e f args fc fd1_ctx.local (Analyses.ask_of_ctx fd1_ctx) in - let rec combine_assign_ctx = - { cd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query combine_assign_ctx q); + let combine_enved = S.combine_env cd_man lv e f args fc fd1_man.local (Analyses.ask_of_man fd1_man) in + let rec combine_assign_man = + { cd_man with + ask = (fun (type a) (q: a Queries.t) -> S.query combine_assign_man q); local = combine_enved; } in - S.D.join acc (S.combine_assign combine_assign_ctx lv e f args fc fd1_ctx.local (Analyses.ask_of_ctx fd1_ctx)) - ) (S.D.bot ()) (S.paths_as_set fd_ctx) + S.D.join acc (S.combine_assign combine_assign_man lv e f args fc fd1_man.local (Analyses.ask_of_man fd1_man)) + ) (S.D.bot ()) (S.paths_as_set fd_man) in if M.tracing then M.traceu "combine" "combined local: %a" S.D.pretty r; r in - let paths = S.enter ctx lv f args in - let paths = List.map (fun (c,v) -> (c, S.context ctx f v, v)) paths in + let paths = S.enter man lv f args in + let paths = List.map (fun (c,v) -> (c, S.context man f v, v)) paths in List.iter (fun (c,fc,v) -> if not (S.D.is_bot v) then sidel (FunctionEntry f, fc) v) paths; let paths = List.map (fun (c,fc,v) -> (c, fc, if S.D.is_bot v then v else getl (Function f, fc))) paths in (* Don't filter bot paths, otherwise LongjmpLifter is not called. *) @@ -233,10 +233,10 @@ struct if M.tracing then M.traceu "combine" "combined: %a" S.D.pretty r; r - let tf_special_call ctx lv f args = S.special ctx lv f args + let tf_special_call man lv f args = S.special man lv f args let tf_proc var edge prev_node lv e args getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in + let man, r, spawns = common_man var edge prev_node d getl sidel getg sideg in let functions = match e with | Lval (Var v, NoOffset) -> @@ -245,7 +245,7 @@ struct [v] | _ -> (* Depends on base for query. *) - let ad = ctx.ask (Queries.EvalFunvar e) in + let ad = man.ask (Queries.EvalFunvar e) in Queries.AD.to_var_may ad (* TODO: don't convert, handle UnknownPtr below *) in let one_function f = @@ -259,11 +259,11 @@ struct begin Some (match Cilfacade.find_varinfo_fundec f with | fd when LibraryFunctions.use_special f.vname -> M.info ~category:Analyzer "Using special for defined function %s" f.vname; - tf_special_call ctx lv f args + tf_special_call man lv f args | fd -> - tf_normal_call ctx lv e fd args getl sidel getg sideg + tf_normal_call man lv e fd args getl sidel getg sideg | exception Not_found -> - tf_special_call ctx lv f args) + tf_special_call man lv f args) end else begin let geq = if var_arg then ">=" else "" in @@ -275,22 +275,22 @@ struct None in let funs = List.filter_map one_function functions in - if [] = funs && not (S.D.is_bot ctx.local) then begin + if [] = funs && not (S.D.is_bot man.local) then begin M.msg_final Warning ~category:Unsound ~tags:[Category Call] "No suitable function to call"; M.warn ~category:Unsound ~tags:[Category Call] "No suitable function to be called at call site. Continuing with state before call."; d (* because LevelSliceLifter *) end else - common_joins ctx funs !r !spawns + common_joins man funs !r !spawns let tf_asm var edge prev_node getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.asm ctx in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns + let man, r, spawns = common_man var edge prev_node d getl sidel getg sideg in + let d = S.asm man in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join man d !r !spawns let tf_skip var edge prev_node getl sidel getg sideg d = - let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = S.skip ctx in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) - common_join ctx d !r !spawns + let man, r, spawns = common_man var edge prev_node d getl sidel getg sideg in + let d = S.skip man in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join man d !r !spawns let tf var getl sidel getg sideg prev_node edge d = begin match edge with @@ -382,13 +382,13 @@ struct let iter_vars getl getg vq fl fg = (* vars for Spec *) - let rec ctx = - { ask = (fun (type a) (q: a Queries.t) -> S.query ctx q) + let rec man = + { ask = (fun (type a) (q: a Queries.t) -> S.query man q) ; emit = (fun _ -> failwith "Cannot \"emit\" in query context.") ; node = MyCFG.dummy_node (* TODO maybe ask should take a node (which could be used here) instead of a location *) ; prev_node = MyCFG.dummy_node - ; control_context = (fun () -> ctx_failwith "No context in query context.") - ; context = (fun () -> ctx_failwith "No context in query context.") + ; control_context = (fun () -> man_failwith "No context in query context.") + ; context = (fun () -> man_failwith "No context in query context.") ; edge = MyCFG.Skip ; local = S.startstate Cil.dummyFunDec.svar (* bot and top both silently raise and catch Deadcode in DeadcodeLifter *) ; global = (fun g -> G.spec (getg (GVar.spec g))) @@ -398,7 +398,7 @@ struct } in let f v = fg (GVar.spec (Obj.obj v)) in - S.query ctx (IterSysVars (vq, f)); + S.query man (IterSysVars (vq, f)); (* node vars for locals *) match vq with diff --git a/src/framework/control.ml b/src/framework/control.ml index 2566939817..0e4a8b1b5d 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -254,7 +254,7 @@ struct in (* add extern variables to local state *) - let do_extern_inits ctx (file : file) : Spec.D.t = + let do_extern_inits man (file : file) : Spec.D.t = let module VS = Set.Make (Basetype.Variables) in let add_glob s = function GVar (v,_,_) -> VS.add v s @@ -262,7 +262,7 @@ struct in let vars = foldGlobals file add_glob VS.empty in let set_bad v st = - Spec.assign {ctx with local = st} (var v) MyCFG.unknown_exp + Spec.assign {man with local = st} (var v) MyCFG.unknown_exp in let is_std = function | {vname = ("__tzname" | "__daylight" | "__timezone"); _} (* unix time.h *) @@ -295,13 +295,13 @@ struct (* analyze cil's global-inits function to get a starting state *) let do_global_inits (file: file) : Spec.D.t * fundec list = - let ctx = + let man = { ask = (fun (type a) (q: a Queries.t) -> Queries.Result.top q) ; emit = (fun _ -> failwith "Cannot \"emit\" in global initializer context.") ; node = MyCFG.dummy_node ; prev_node = MyCFG.dummy_node - ; control_context = (fun () -> ctx_failwith "Global initializers have no context.") - ; context = (fun () -> ctx_failwith "Global initializers have no context.") + ; control_context = (fun () -> man_failwith "Global initializers have no context.") + ; context = (fun () -> man_failwith "Global initializers have no context.") ; edge = MyCFG.Skip ; local = Spec.D.top () ; global = (fun g -> EQSys.G.spec (getg (EQSys.GVar.spec g))) @@ -322,7 +322,7 @@ struct match edge with | MyCFG.Entry func -> if M.tracing then M.trace "global_inits" "Entry %a" d_lval (var func.svar); - Spec.body {ctx with local = st} func + Spec.body {man with local = st} func | MyCFG.Assign (lval,exp) -> if M.tracing then M.trace "global_inits" "Assign %a = %a" d_lval lval d_exp exp; begin match lval, exp with @@ -331,14 +331,14 @@ struct (try funs := Cilfacade.find_varinfo_fundec f :: !funs with Not_found -> ()) | _ -> () end; - let res = Spec.assign {ctx with local = st} lval exp in + let res = Spec.assign {man with local = st} lval exp in (* Needed for privatizations (e.g. None) that do not side immediately *) - let res' = Spec.sync {ctx with local = res} `Normal in + let res' = Spec.sync {man with local = res} `Normal in if M.tracing then M.trace "global_inits" "\t\t -> state:%a" Spec.D.pretty res; res' | _ -> failwith "Unsupported global initializer edge" in - let with_externs = do_extern_inits ctx file in + let with_externs = do_extern_inits man file in (*if (get_bool "dbg.verbose") then Printf.printf "Number of init. edges : %d\nWorking:" (List.length edges); *) let old_loc = !Goblint_tracing.current_loc in let result : Spec.D.t = List.fold_left transfer_func with_externs edges in @@ -401,12 +401,12 @@ struct let enter_with st fd = let st = st fd.svar in - let ctx = + let man = { ask = (fun (type a) (q: a Queries.t) -> Queries.Result.top q) ; emit = (fun _ -> failwith "Cannot \"emit\" in enter_with context.") ; node = MyCFG.dummy_node ; prev_node = MyCFG.dummy_node - ; control_context = (fun () -> ctx_failwith "enter_with has no control_context.") + ; control_context = (fun () -> man_failwith "enter_with has no control_context.") ; context = Spec.startcontext ; edge = MyCFG.Skip ; local = st @@ -417,7 +417,7 @@ struct } in let args = List.map (fun x -> MyCFG.unknown_exp) fd.sformals in - let ents = Spec.enter ctx None fd args in + let ents = Spec.enter man None fd args in List.map (fun (_,s) -> fd, s) ents in @@ -433,13 +433,13 @@ struct let exitvars = List.map (enter_with Spec.exitstate) exitfuns in let otherstate st v = - let ctx = + let man = { ask = (fun (type a) (q: a Queries.t) -> Queries.Result.top q) ; emit = (fun _ -> failwith "Cannot \"emit\" in otherstate context.") ; node = MyCFG.dummy_node ; prev_node = MyCFG.dummy_node - ; control_context = (fun () -> ctx_failwith "enter_func has no context.") - ; context = (fun () -> ctx_failwith "enter_func has no context.") + ; control_context = (fun () -> man_failwith "enter_func has no context.") + ; context = (fun () -> man_failwith "enter_func has no context.") ; edge = MyCFG.Skip ; local = st ; global = (fun g -> EQSys.G.spec (getg (EQSys.GVar.spec g))) @@ -449,7 +449,7 @@ struct } in (* TODO: don't hd *) - List.hd (Spec.threadenter ctx ~multiple:false None v []) + List.hd (Spec.threadenter man ~multiple:false None v []) (* TODO: do threadspawn to mainfuns? *) in let prestartstate = Spec.startstate MyCFG.dummy_func.svar in (* like in do_extern_inits *) @@ -460,12 +460,12 @@ struct AnalysisState.global_initialization := false; - let ctx e = + let man e = { ask = (fun (type a) (q: a Queries.t) -> Queries.Result.top q) ; emit = (fun _ -> failwith "Cannot \"emit\" in enter_with context.") ; node = MyCFG.dummy_node ; prev_node = MyCFG.dummy_node - ; control_context = (fun () -> ctx_failwith "enter_with has no control_context.") + ; control_context = (fun () -> man_failwith "enter_with has no control_context.") ; context = Spec.startcontext ; edge = MyCFG.Skip ; local = e @@ -477,12 +477,12 @@ struct in let startvars' = if get_bool "exp.forward" then - List.map (fun (n,e) -> (MyCFG.FunctionEntry n, Spec.context (ctx e) n e)) startvars + List.map (fun (n,e) -> (MyCFG.FunctionEntry n, Spec.context (man e) n e)) startvars else - List.map (fun (n,e) -> (MyCFG.Function n, Spec.context (ctx e) n e)) startvars + List.map (fun (n,e) -> (MyCFG.Function n, Spec.context (man e) n e)) startvars in - let entrystates = List.map (fun (n,e) -> (MyCFG.FunctionEntry n, Spec.context (ctx e) n e), e) startvars in + let entrystates = List.map (fun (n,e) -> (MyCFG.FunctionEntry n, Spec.context (man e) n e), e) startvars in let entrystates_global = GHT.to_list gh in let uncalled_dead = ref 0 in diff --git a/src/framework/resultQuery.ml b/src/framework/resultQuery.ml index c676c41c14..ea4a75f40a 100644 --- a/src/framework/resultQuery.ml +++ b/src/framework/resultQuery.ml @@ -7,9 +7,9 @@ struct open SpecSys let ask_local (gh: EQSys.G.t GHT.t) (lvar:EQSys.LVar.t) local = - (* build a ctx for using the query system *) - let rec ctx = - { ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx q) + (* build a man for using the query system *) + let rec man = + { ask = (fun (type a) (q: a Queries.t) -> Spec.query man q) ; emit = (fun _ -> failwith "Cannot \"emit\" in witness context.") ; node = fst lvar ; prev_node = MyCFG.dummy_node @@ -23,17 +23,17 @@ struct ; sideg = (fun v g -> failwith "Cannot \"sideg\" in witness context.") } in - Spec.query ctx + Spec.query man let ask_local_node (gh: EQSys.G.t GHT.t) (n: Node.t) local = - (* build a ctx for using the query system *) - let rec ctx = - { ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx q) + (* build a man for using the query system *) + let rec man = + { ask = (fun (type a) (q: a Queries.t) -> Spec.query man q) ; emit = (fun _ -> failwith "Cannot \"emit\" in witness context.") ; node = n ; prev_node = MyCFG.dummy_node - ; control_context = (fun () -> ctx_failwith "No context in witness context.") - ; context = (fun () -> ctx_failwith "No context in witness context.") + ; control_context = (fun () -> man_failwith "No context in witness context.") + ; context = (fun () -> man_failwith "No context in witness context.") ; edge = MyCFG.Skip ; local = local ; global = (fun g -> try EQSys.G.spec (GHT.find gh (EQSys.GVar.spec g)) with Not_found -> Spec.G.bot ()) (* TODO: how can be missing? *) @@ -42,18 +42,18 @@ struct ; sideg = (fun v g -> failwith "Cannot \"sideg\" in witness context.") } in - Spec.query ctx + Spec.query man let ask_global (gh: EQSys.G.t GHT.t) = (* copied from Control for WarnGlobal *) - (* build a ctx for using the query system *) - let rec ctx = - { ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx q) + (* build a man for using the query system *) + let rec man = + { ask = (fun (type a) (q: a Queries.t) -> Spec.query man q) ; emit = (fun _ -> failwith "Cannot \"emit\" in query context.") ; node = MyCFG.dummy_node (* TODO maybe ask should take a node (which could be used here) instead of a location *) ; prev_node = MyCFG.dummy_node - ; control_context = (fun () -> ctx_failwith "No context in query context.") - ; context = (fun () -> ctx_failwith "No context in query context.") + ; control_context = (fun () -> man_failwith "No context in query context.") + ; context = (fun () -> man_failwith "No context in query context.") ; edge = MyCFG.Skip ; local = Spec.startstate GoblintCil.dummyFunDec.svar (* bot and top both silently raise and catch Deadcode in DeadcodeLifter *) (* TODO: is this startstate bad? *) ; global = (fun v -> EQSys.G.spec (try GHT.find gh (EQSys.GVar.spec v) with Not_found -> EQSys.G.bot ())) (* TODO: how can be missing? *) @@ -62,7 +62,7 @@ struct ; sideg = (fun v g -> failwith "Cannot \"split\" in query context.") } in - Spec.query ctx + Spec.query man end diff --git a/src/lifters/contextGasLifter.ml b/src/lifters/contextGasLifter.ml index 75bd9f7641..dc1a9e1565 100644 --- a/src/lifters/contextGasLifter.ml +++ b/src/lifters/contextGasLifter.ml @@ -42,8 +42,8 @@ struct let of_elt (x, _) = of_elt x end - (* returns context gas value of the given ctx *) - let cg_val ctx = snd ctx.local + (* returns context gas value of the given man *) + let cg_val man = snd man.local type marshal = S.marshal let init = S.init @@ -56,47 +56,47 @@ struct let exitstate v = S.exitstate v, Gas.startgas () let morphstate v (d,i) = S.morphstate v d, i - let conv (ctx:(D.t,G.t,C.t,V.t) ctx): (S.D.t,G.t,S.C.t,V.t)ctx = - {ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, cg_val ctx) es) - ; context = (fun () -> match ctx.context () with Some c -> c | None -> ctx_failwith "no context (contextGas = 0)")} + let conv (man:(D.t,G.t,C.t,V.t) man): (S.D.t,G.t,S.C.t,V.t) man = + {man with local = fst man.local + ; split = (fun d es -> man.split (d, cg_val man) es) + ; context = (fun () -> match man.context () with Some c -> c | None -> man_failwith "no context (contextGas = 0)")} - let context ctx fd (d,i) = + let context man fd (d,i) = (* only keep context if the context gas is greater zero *) if Gas.is_exhausted fd i then None else - Some (S.context (conv ctx) fd d) + Some (S.context (conv man) fd d) - let enter ctx r f args = - let liftmap_tup = List.map (fun (x,y) -> (x, cg_val ctx), (y, Gas.callee_gas f (cg_val ctx))) in - liftmap_tup (S.enter (conv ctx) r f args) + let enter man r f args = + let liftmap_tup = List.map (fun (x,y) -> (x, cg_val man), (y, Gas.callee_gas f (cg_val man))) in + liftmap_tup (S.enter (conv man) r f args) - let threadenter ctx ~multiple lval f args = - let liftmap d = List.map (fun (x) -> (x, Gas.thread_gas f (cg_val ctx))) d in - liftmap (S.threadenter (conv ctx) ~multiple lval f args) + let threadenter man ~multiple lval f args = + let liftmap d = List.map (fun (x) -> (x, Gas.thread_gas f (cg_val man))) d in + liftmap (S.threadenter (conv man) ~multiple lval f args) - let query ctx (type a) (q: a Queries.t):a Queries.result = + let query man (type a) (q: a Queries.t):a Queries.result = match q with | Queries.GasExhausted f -> - let (d,i) = ctx.local in + let (d,i) = man.local in Gas.is_exhausted f i - | _ -> S.query (conv ctx) q - - let sync ctx reason = S.sync (conv ctx) reason, cg_val ctx - let assign ctx lval expr = S.assign (conv ctx) lval expr, cg_val ctx - let vdecl ctx v = S.vdecl (conv ctx) v, cg_val ctx - let body ctx fundec = S.body (conv ctx) fundec, cg_val ctx - let branch ctx e tv = S.branch (conv ctx) e tv, cg_val ctx - let return ctx r f = S.return (conv ctx) r f, cg_val ctx - let asm ctx = S.asm (conv ctx), cg_val ctx - let skip ctx = S.skip (conv ctx), cg_val ctx - let special ctx r f args = S.special (conv ctx) r f args, cg_val ctx - let combine_env ctx r fe f args fc es f_ask = S.combine_env (conv ctx) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val ctx - let combine_assign ctx r fe f args fc es f_ask = S.combine_assign (conv ctx) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val ctx - let paths_as_set ctx = List.map (fun (x) -> (x, cg_val ctx)) @@ S.paths_as_set (conv ctx) - let threadspawn ctx ~multiple lval f args fctx = S.threadspawn (conv ctx) ~multiple lval f args (conv fctx), cg_val ctx - let event ctx e octx = S.event (conv ctx) e (conv octx), cg_val ctx + | _ -> S.query (conv man) q + + let sync man reason = S.sync (conv man) reason, cg_val man + let assign man lval expr = S.assign (conv man) lval expr, cg_val man + let vdecl man v = S.vdecl (conv man) v, cg_val man + let body man fundec = S.body (conv man) fundec, cg_val man + let branch man e tv = S.branch (conv man) e tv, cg_val man + let return man r f = S.return (conv man) r f, cg_val man + let asm man = S.asm (conv man), cg_val man + let skip man = S.skip (conv man), cg_val man + let special man r f args = S.special (conv man) r f args, cg_val man + let combine_env man r fe f args fc es f_ask = S.combine_env (conv man) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val man + let combine_assign man r fe f args fc es f_ask = S.combine_assign (conv man) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val man + let paths_as_set man = List.map (fun (x) -> (x, cg_val man)) @@ S.paths_as_set (conv man) + let threadspawn man ~multiple lval f args fman = S.threadspawn (conv man) ~multiple lval f args (conv fman), cg_val man + let event man e oman = S.event (conv man) e (conv oman), cg_val man end let get_gas_lifter () = diff --git a/src/lifters/longjmpLifter.ml b/src/lifters/longjmpLifter.ml index a093f8c703..c392499ef6 100644 --- a/src/lifters/longjmpLifter.ml +++ b/src/lifters/longjmpLifter.ml @@ -42,19 +42,19 @@ struct | x -> BatPrintf.fprintf f "%a" printXml x end - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); + let conv (man: (_, G.t, _, V.t) man): (_, S.G.t, _, S.V.t) man = + { man with + global = (fun v -> G.s (man.global (V.s v))); + sideg = (fun v g -> man.sideg (V.s v) (G.create_s g)); } - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal g -> let g: V.t = Obj.obj g in begin match g with | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) + S.query (conv man) (WarnGlobal (Obj.repr g)) | _ -> Queries.Result.top q end @@ -62,7 +62,7 @@ struct let g: V.t = Obj.obj g in begin match g with | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) + S.query (conv man) (InvariantGlobal (Obj.repr g)) | _ -> Queries.Result.top q end @@ -70,89 +70,89 @@ struct let g: V.t = Obj.obj g in begin match g with | `Left g -> - S.query (conv ctx) (YamlEntryGlobal (Obj.repr g, task)) + S.query (conv man) (YamlEntryGlobal (Obj.repr g, task)) | _ -> Queries.Result.top q end | IterSysVars (vq, vf) -> (* vars for S *) let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); + S.query (conv man) (IterSysVars (vq, vf')); (* TODO: vars? *) | _ -> - S.query (conv ctx) q + S.query (conv man) q - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let context ctx = S.context (conv ctx) + let branch man = S.branch (conv man) + let assign man = S.assign (conv man) + let vdecl man = S.vdecl (conv man) + let enter man = S.enter (conv man) + let paths_as_set man = S.paths_as_set (conv man) + let body man = S.body (conv man) + let return man = S.return (conv man) + let context man = S.context (conv man) - let combine_env ctx lv e f args fc fd f_ask = - let conv_ctx = conv ctx in - let current_fundec = Node.find_fundec ctx.node in + let combine_env man lv e f args fc fd f_ask = + let conv_man = conv man in + let current_fundec = Node.find_fundec man.node in let handle_longjmp (cd, fc, longfd) = (* This is called per-path. *) - let rec cd_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); + let rec cd_man = + { conv_man with + ask = (fun (type a) (q: a Queries.t) -> S.query cd_man q); local = cd; } in - let longfd_ctx = - (* Inner scope to prevent unsynced longfd_ctx from being used. *) + let longfd_man = + (* Inner scope to prevent unsynced longfd_man from being used. *) (* Extra sync like with normal combine. *) - let rec sync_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query sync_ctx q); + let rec sync_man = + { conv_man with + ask = (fun (type a) (q: a Queries.t) -> S.query sync_man q); local = longfd; prev_node = Function f; } in - let synced = S.sync sync_ctx `Join in - let rec longfd_ctx = - { sync_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query longfd_ctx q); + let synced = S.sync sync_man `Join in + let rec longfd_man = + { sync_man with + ask = (fun (type a) (q: a Queries.t) -> S.query longfd_man q); local = synced; } in - longfd_ctx + longfd_man in let combined = lazy ( (* does not depend on target, do at most once *) (* Globals are non-problematic here, as they are always carried around without any issues! *) (* A combine call is mostly needed to ensure locals have appropriate values. *) (* Using f from called function on purpose here! Needed? *) - S.combine_env cd_ctx None e f args fc longfd_ctx.local (Analyses.ask_of_ctx longfd_ctx) (* no lval because longjmp return skips return value assignment *) + S.combine_env cd_man None e f args fc longfd_man.local (Analyses.ask_of_man longfd_man) (* no lval because longjmp return skips return value assignment *) ) in let returned = lazy ( (* does not depend on target, do at most once *) - let rec combined_ctx = - { cd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query combined_ctx q); + let rec combined_man = + { cd_man with + ask = (fun (type a) (q: a Queries.t) -> S.query combined_man q); local = Lazy.force combined; } in - S.return combined_ctx None current_fundec + S.return combined_man None current_fundec ) in - let (active_targets, _) = longfd_ctx.ask ActiveJumpBuf in - let valid_targets = cd_ctx.ask ValidLongJmp in + let (active_targets, _) = longfd_man.ask ActiveJumpBuf in + let valid_targets = cd_man.ask ValidLongJmp in let handle_target target = match target with | JmpBufDomain.BufferEntryOrTop.AllTargets -> () (* The warning is already emitted at the point where the longjmp happens *) | Target (target_node, target_context) -> let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( + if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (man.control_context ()) then ( if M.tracing then Messages.tracel "longjmp" "Fun: Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force combined)) + man.sideg (V.longjmpto (target_node, man.context ())) (G.create_local (Lazy.force combined)) (* No need to propagate this outwards here, the set of valid longjumps is part of the context, we can never have the same context setting the longjmp multiple times *) ) (* Appropriate setjmp is not in current function & current context *) else if JmpBufDomain.JmpBufSet.mem target valid_targets then - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) + man.sideg (V.longjmpret (current_fundec, man.context ())) (G.create_local (Lazy.force returned)) else (* It actually is not handled here but was propagated here spuriously, we already warned at the location where this issue is caused *) (* As the validlongjumps inside the callee is a a superset of the ones inside the caller *) @@ -161,60 +161,60 @@ struct JmpBufDomain.JmpBufSet.iter handle_target active_targets in if M.tracing then M.tracel "longjmp" "longfd getg %a" CilType.Fundec.pretty f; - let longfd = G.local (ctx.global (V.longjmpret (f, Option.get fc))) in + let longfd = G.local (man.global (V.longjmpret (f, Option.get fc))) in if M.tracing then M.tracel "longjmp" "longfd %a" D.pretty longfd; if not (D.is_bot longfd) then - handle_longjmp (ctx.local, fc, longfd); - S.combine_env (conv_ctx) lv e f args fc fd f_ask + handle_longjmp (man.local, fc, longfd); + S.combine_env (conv_man) lv e f args fc fd f_ask - let combine_assign ctx lv e f args fc fd f_ask = - S.combine_assign (conv ctx) lv e f args fc fd f_ask + let combine_assign man lv e f args fc fd f_ask = + S.combine_assign (conv man) lv e f args fc fd f_ask - let special ctx lv f args = - let conv_ctx = conv ctx in + let special man lv f args = + let conv_man = conv man in match (LibraryFunctions.find f).special args with | Setjmp {env} -> (* Handling of returning for the first time *) - let normal_return = S.special conv_ctx lv f args in - let jmp_return = G.local (ctx.global (V.longjmpto (ctx.prev_node, ctx.context ()))) in + let normal_return = S.special conv_man lv f args in + let jmp_return = G.local (man.global (V.longjmpto (man.prev_node, man.context ()))) in if S.D.is_bot jmp_return then normal_return else ( - let rec jmp_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query jmp_ctx q); + let rec jmp_man = + { conv_man with + ask = (fun (type a) (q: a Queries.t) -> S.query jmp_man q); local = jmp_return; } in - let longjmped = S.event jmp_ctx (Events.Longjmped {lval=lv}) jmp_ctx in + let longjmped = S.event jmp_man (Events.Longjmped {lval=lv}) jmp_man in S.D.join normal_return longjmped ) | Longjmp {env; value} -> - let current_fundec = Node.find_fundec ctx.node in + let current_fundec = Node.find_fundec man.node in let handle_path path = ( - let rec path_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query path_ctx q); + let rec path_man = + { conv_man with + ask = (fun (type a) (q: a Queries.t) -> S.query path_man q); local = path; } in let specialed = lazy ( (* does not depend on target, do at most once *) - S.special path_ctx lv f args + S.special path_man lv f args ) in let returned = lazy ( (* does not depend on target, do at most once *) - let rec specialed_ctx = - { path_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query specialed_ctx q); + let rec specialed_man = + { path_man with + ask = (fun (type a) (q: a Queries.t) -> S.query specialed_man q); local = Lazy.force specialed; } in - S.return specialed_ctx None current_fundec + S.return specialed_man None current_fundec ) in - (* Eval `env` again to avoid having to construct bespoke ctx to ask *) - let targets = path_ctx.ask (EvalJumpBuf env) in - let valid_targets = path_ctx.ask ValidLongJmp in + (* Eval `env` again to avoid having to construct bespoke man to ask *) + let targets = path_man.ask (EvalJumpBuf env) in + let valid_targets = path_man.ask ValidLongJmp in if M.tracing then Messages.tracel "longjmp" "Jumping to %a" JmpBufDomain.JmpBufSet.pretty targets; let handle_target target = match target with | JmpBufDomain.BufferEntryOrTop.AllTargets -> @@ -222,13 +222,13 @@ struct M.msg_final Error ~category:Unsound ~tags:[Category Imprecise; Category Call] "Longjmp to unknown target ignored" | Target (target_node, target_context) -> let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( + if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (man.control_context ()) then ( if M.tracing then Messages.tracel "longjmp" "Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force specialed)) + man.sideg (V.longjmpto (target_node, man.context ())) (G.create_local (Lazy.force specialed)) ) else if JmpBufDomain.JmpBufSet.mem target valid_targets then ( - if M.tracing then Messages.tracel "longjmp" "Longjmp to somewhere else, side-effect to %i" (S.C.hash (ctx.context ())); - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) + if M.tracing then Messages.tracel "longjmp" "Longjmp to somewhere else, side-effect to %i" (S.C.hash (man.context ())); + man.sideg (V.longjmpret (current_fundec, man.context ())) (G.create_local (Lazy.force returned)) ) else M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target! (Target %a in Function %a which may have already returned or is in a different thread)" Node.pretty target_node CilType.Fundec.pretty target_fundec @@ -239,17 +239,17 @@ struct JmpBufDomain.JmpBufSet.iter handle_target targets ) in - List.iter handle_path (S.paths_as_set conv_ctx); + List.iter handle_path (S.paths_as_set conv_man); if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( AnalysisState.svcomp_may_not_terminate := true; M.warn ~category:Termination "The program might not terminate! (Longjmp)" ); S.D.bot () - | _ -> S.special conv_ctx lv f args - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) + | _ -> S.special conv_man lv f args + let threadenter man = S.threadenter (conv man) + let threadspawn man ~multiple lv f args fman = S.threadspawn (conv man) ~multiple lv f args (conv fman) + let sync man = S.sync (conv man) + let skip man = S.skip (conv man) + let asm man = S.asm (conv man) + let event man e oman = S.event (conv man) e (conv oman) end diff --git a/src/lifters/recursionTermLifter.ml b/src/lifters/recursionTermLifter.ml index 37522305b9..f694ecb1e2 100644 --- a/src/lifters/recursionTermLifter.ml +++ b/src/lifters/recursionTermLifter.ml @@ -55,13 +55,13 @@ struct let name () = "RecursionTermLifter (" ^ S.name () ^ ")" - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.spec (ctx.global (V.spec v))); - sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_spec g)); + let conv (man: (_, G.t, _, V.t) man): (_, S.G.t, _, S.V.t) man = + { man with + global = (fun v -> G.spec (man.global (V.spec v))); + sideg = (fun v g -> man.sideg (V.spec v) (G.create_spec g)); } - let cycleDetection ctx call = + let cycleDetection man call = let module LH = Hashtbl.Make (Printable.Prod (CilType.Fundec) (S.C)) in let module LS = Set.Make (Printable.Prod (CilType.Fundec) (S.C)) in (* find all cycles/SCCs *) @@ -78,7 +78,7 @@ struct LH.replace global_visited_calls call (); let new_path_visited_calls = LS.add call path_visited_calls in let gvar = V.call call in - let callers = G.callers (ctx.global gvar) in + let callers = G.callers (man.global gvar) in CallerSet.iter (fun to_call -> iter_call new_path_visited_calls to_call ) callers; @@ -86,23 +86,23 @@ struct in iter_call LS.empty call - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal v -> (* check result of loop analysis *) - if not (ctx.ask Queries.MustTermAllLoops) then + if not (man.ask Queries.MustTermAllLoops) then AnalysisState.svcomp_may_not_terminate := true; let v: V.t = Obj.obj v in begin match v with | `Left v' -> - S.query (conv ctx) (WarnGlobal (Obj.repr v')) - | `Right call -> cycleDetection ctx call (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) + S.query (conv man) (WarnGlobal (Obj.repr v')) + | `Right call -> cycleDetection man call (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) end | InvariantGlobal v -> let v: V.t = Obj.obj v in begin match v with | `Left v -> - S.query (conv ctx) (InvariantGlobal (Obj.repr v)) + S.query (conv man) (InvariantGlobal (Obj.repr v)) | `Right v -> Queries.Result.top q end @@ -110,44 +110,44 @@ struct let v: V.t = Obj.obj v in begin match v with | `Left v -> - S.query (conv ctx) (YamlEntryGlobal (Obj.repr v, task)) + S.query (conv man) (YamlEntryGlobal (Obj.repr v, task)) | `Right v -> Queries.Result.top q end - | _ -> S.query (conv ctx) q + | _ -> S.query (conv man) q - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) + let branch man = S.branch (conv man) + let assign man = S.assign (conv man) + let vdecl man = S.vdecl (conv man) let record_call sideg callee caller = sideg (V.call callee) (G.create_singleton_caller caller) - let enter ctx = S.enter (conv ctx) - let context ctx = S.context (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx r fe f args fc es f_ask = + let enter man = S.enter (conv man) + let context man = S.context (conv man) + let paths_as_set man = S.paths_as_set (conv man) + let body man = S.body (conv man) + let return man = S.return (conv man) + let combine_env man r fe f args fc es f_ask = if !AnalysisState.postsolving then ( - let c_r: S.C.t = ctx.context () in (* Caller context *) - let nodeF = ctx.node in + let c_r: S.C.t = man.context () in (* Caller context *) + let nodeF = man.node in let fd_r : fundec = Node.find_fundec nodeF in (* Caller fundec *) let caller: (fundec * S.C.t) = (fd_r, c_r) in let c_e: S.C.t = Option.get fc in (* Callee context *) let fd_e : fundec = f in (* Callee fundec *) let callee = (fd_e, c_e) in - record_call ctx.sideg callee caller + record_call man.sideg callee caller ); - S.combine_env (conv ctx) r fe f args fc es f_ask - - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) + S.combine_env (conv man) r fe f args fc es f_ask + + let combine_assign man = S.combine_assign (conv man) + let special man = S.special (conv man) + let threadenter man = S.threadenter (conv man) + let threadspawn man ~multiple lv f args fman = S.threadspawn (conv man) ~multiple lv f args (conv fman) + let sync man = S.sync (conv man) + let skip man = S.skip (conv man) + let asm man = S.asm (conv man) + let event man e oman = S.event (conv man) e (conv oman) end diff --git a/src/lifters/specLifters.ml b/src/lifters/specLifters.ml index 39c7799395..45102d0056 100644 --- a/src/lifters/specLifters.ml +++ b/src/lifters/specLifters.ml @@ -38,64 +38,64 @@ struct let exitstate v = D.lift (S.exitstate v) let morphstate v d = D.lift (S.morphstate v (D.unlift d)) - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) + let conv man = + { man with local = D.unlift man.local + ; split = (fun d es -> man.split (D.lift d) es ) } - let context ctx fd = S.context (conv ctx) fd % D.unlift + let context man fd = S.context (conv man) fd % D.unlift let startcontext () = S.startcontext () - let sync ctx reason = - D.lift @@ S.sync (conv ctx) reason + let sync man reason = + D.lift @@ S.sync (conv man) reason - let query ctx = - S.query (conv ctx) + let query man = + S.query (conv man) - let assign ctx lv e = - D.lift @@ S.assign (conv ctx) lv e + let assign man lv e = + D.lift @@ S.assign (conv man) lv e - let vdecl ctx v = - D.lift @@ S.vdecl (conv ctx) v + let vdecl man v = + D.lift @@ S.vdecl (conv man) v - let branch ctx e tv = - D.lift @@ S.branch (conv ctx) e tv + let branch man e tv = + D.lift @@ S.branch (conv man) e tv - let body ctx f = - D.lift @@ S.body (conv ctx) f + let body man f = + D.lift @@ S.body (conv man) f - let return ctx r f = - D.lift @@ S.return (conv ctx) r f + let return man r f = + D.lift @@ S.return (conv man) r f - let asm ctx = - D.lift @@ S.asm (conv ctx) + let asm man = + D.lift @@ S.asm (conv man) - let skip ctx = - D.lift @@ S.skip (conv ctx) + let skip man = + D.lift @@ S.skip (conv man) - let enter ctx r f args = - List.map (fun (x,y) -> D.lift x, D.lift y) @@ S.enter (conv ctx) r f args + let enter man r f args = + List.map (fun (x,y) -> D.lift x, D.lift y) @@ S.enter (conv man) r f args - let special ctx r f args = - D.lift @@ S.special (conv ctx) r f args + let special man r f args = + D.lift @@ S.special (conv man) r f args - let combine_env ctx r fe f args fc es f_ask = - D.lift @@ S.combine_env (conv ctx) r fe f args fc (D.unlift es) f_ask + let combine_env man r fe f args fc es f_ask = + D.lift @@ S.combine_env (conv man) r fe f args fc (D.unlift es) f_ask - let combine_assign ctx r fe f args fc es f_ask = - D.lift @@ S.combine_assign (conv ctx) r fe f args fc (D.unlift es) f_ask + let combine_assign man r fe f args fc es f_ask = + D.lift @@ S.combine_assign (conv man) r fe f args fc (D.unlift es) f_ask - let threadenter ctx ~multiple lval f args = - List.map D.lift @@ S.threadenter (conv ctx) ~multiple lval f args + let threadenter man ~multiple lval f args = + List.map D.lift @@ S.threadenter (conv man) ~multiple lval f args - let threadspawn ctx ~multiple lval f args fctx = - D.lift @@ S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) + let threadspawn man ~multiple lval f args fman = + D.lift @@ S.threadspawn (conv man) ~multiple lval f args (conv fman) - let paths_as_set ctx = - List.map (fun x -> D.lift x) @@ S.paths_as_set (conv ctx) + let paths_as_set man = + List.map (fun x -> D.lift x) @@ S.paths_as_set (conv man) - let event ctx e octx = - D.lift @@ S.event (conv ctx) e (conv octx) + let event man e oman = + D.lift @@ S.event (conv man) e (conv oman) end (** Lifts a [Spec] so that the context is [Hashcons]d. *) @@ -121,63 +121,63 @@ struct let exitstate = S.exitstate let morphstate = S.morphstate - let conv ctx = - { ctx with context = (fun () -> C.unlift (ctx.context ())) } + let conv man = + { man with context = (fun () -> C.unlift (man.context ())) } - let context ctx fd = C.lift % S.context (conv ctx) fd + let context man fd = C.lift % S.context (conv man) fd let startcontext () = C.lift @@ S.startcontext () - let sync ctx reason = - S.sync (conv ctx) reason + let sync man reason = + S.sync (conv man) reason - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | Queries.IterPrevVars f -> let g i (n, c, j) e = f i (n, Obj.repr (C.lift (Obj.obj c)), j) e in - S.query (conv ctx) (Queries.IterPrevVars g) - | _ -> S.query (conv ctx) q + S.query (conv man) (Queries.IterPrevVars g) + | _ -> S.query (conv man) q - let assign ctx lv e = - S.assign (conv ctx) lv e + let assign man lv e = + S.assign (conv man) lv e - let vdecl ctx v = - S.vdecl (conv ctx) v + let vdecl man v = + S.vdecl (conv man) v - let branch ctx e tv = - S.branch (conv ctx) e tv + let branch man e tv = + S.branch (conv man) e tv - let body ctx f = - S.body (conv ctx) f + let body man f = + S.body (conv man) f - let return ctx r f = - S.return (conv ctx) r f + let return man r f = + S.return (conv man) r f - let asm ctx = - S.asm (conv ctx) + let asm man = + S.asm (conv man) - let skip ctx = - S.skip (conv ctx) + let skip man = + S.skip (conv man) - let enter ctx r f args = - S.enter (conv ctx) r f args + let enter man r f args = + S.enter (conv man) r f args - let special ctx r f args = - S.special (conv ctx) r f args + let special man r f args = + S.special (conv man) r f args - let combine_env ctx r fe f args fc es f_ask = - S.combine_env (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask + let combine_env man r fe f args fc es f_ask = + S.combine_env (conv man) r fe f args (Option.map C.unlift fc) es f_ask - let combine_assign ctx r fe f args fc es f_ask = - S.combine_assign (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask + let combine_assign man r fe f args fc es f_ask = + S.combine_assign (conv man) r fe f args (Option.map C.unlift fc) es f_ask - let threadenter ctx ~multiple lval f args = - S.threadenter (conv ctx) ~multiple lval f args + let threadenter man ~multiple lval f args = + S.threadenter (conv man) ~multiple lval f args - let threadspawn ctx ~multiple lval f args fctx = - S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) + let threadspawn man ~multiple lval f args fman = + S.threadspawn (conv man) ~multiple lval f args (conv fman) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) + let paths_as_set man = S.paths_as_set (conv man) + let event man e oman = S.event (conv man) e (conv oman) end (* see option ana.opt.equal *) @@ -221,40 +221,40 @@ struct let exitstate v = (S.exitstate v, !start_level) let morphstate v (d,l) = (S.morphstate v d, l) - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) + let conv man = + { man with local = fst man.local + ; split = (fun d es -> man.split (d, snd man.local) es ) } - let context ctx fd (d,_) = S.context (conv ctx) fd d + let context man fd (d,_) = S.context (conv man) fd d let startcontext () = S.startcontext () - let lift_fun ctx f g h = - f @@ h (g (conv ctx)) + let lift_fun man f g h = + f @@ h (g (conv man)) - let enter' ctx r f args = - let liftmap = List.map (fun (x,y) -> (x, snd ctx.local), (y, snd ctx.local)) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) + let enter' man r f args = + let liftmap = List.map (fun (x,y) -> (x, snd man.local), (y, snd man.local)) in + lift_fun man liftmap S.enter ((|>) args % (|>) f % (|>) r) - let lift ctx d = (d, snd ctx.local) + let lift man d = (d, snd man.local) let lift_start_level d = (d, !start_level) - let sync ctx reason = lift_fun ctx (lift ctx) S.sync ((|>) reason) - let query' ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun x -> x q) - let assign ctx lv e = lift_fun ctx (lift ctx) S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx (lift ctx) S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx (lift ctx) S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx (lift ctx) S.body ((|>) f) - let return ctx r f = lift_fun ctx (lift ctx) S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx (lift ctx) S.asm identity - let skip ctx = lift_fun ctx (lift ctx) S.skip identity - let special ctx r f args = lift_fun ctx (lift ctx) S.special ((|>) args % (|>) f % (|>) r) - let combine_env' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (lift ctx) (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) + let sync man reason = lift_fun man (lift man) S.sync ((|>) reason) + let query' man (type a) (q: a Queries.t): a Queries.result = + lift_fun man identity S.query (fun x -> x q) + let assign man lv e = lift_fun man (lift man) S.assign ((|>) e % (|>) lv) + let vdecl man v = lift_fun man (lift man) S.vdecl ((|>) v) + let branch man e tv = lift_fun man (lift man) S.branch ((|>) tv % (|>) e) + let body man f = lift_fun man (lift man) S.body ((|>) f) + let return man r f = lift_fun man (lift man) S.return ((|>) f % (|>) r) + let asm man = lift_fun man (lift man) S.asm identity + let skip man = lift_fun man (lift man) S.skip identity + let special man r f args = lift_fun man (lift man) S.special ((|>) args % (|>) f % (|>) r) + let combine_env' man r fe f args fc es f_ask = lift_fun man (lift man) S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) + let combine_assign' man r fe f args fc es f_ask = lift_fun man (lift man) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) + + let threadenter man ~multiple lval f args = lift_fun man (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) + let threadspawn man ~multiple lval f args fman = lift_fun man (lift man) (S.threadspawn ~multiple) ((|>) (conv fman) % (|>) args % (|>) f % (|>) lval) let leq0 = function | `Top -> false @@ -269,47 +269,47 @@ struct | `Lifted x -> `Lifted (Int64.add x 1L) | x -> x - let paths_as_set ctx = - let liftmap = List.map (fun x -> (x, snd ctx.local)) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) + let paths_as_set man = + let liftmap = List.map (fun x -> (x, snd man.local)) in + lift_fun man liftmap S.paths_as_set (Fun.id) - let event ctx e octx = - lift_fun ctx (lift ctx) S.event ((|>) (conv octx) % (|>) e) + let event man e oman = + lift_fun man (lift man) S.event ((|>) (conv oman) % (|>) e) - let enter ctx r f args = - let (d,l) = ctx.local in + let enter man r f args = + let (d,l) = man.local in if leq0 l then - [ctx.local, D.bot ()] + [man.local, D.bot ()] else - enter' {ctx with local=(d, sub1 l)} r f args + enter' {man with local=(d, sub1 l)} r f args - let combine_env ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in + let combine_env man r fe f args fc es f_ask = + let (d,l) = man.local in let l = add1 l in if leq0 l then (d, l) else - let d',_ = combine_env' ctx r fe f args fc es f_ask in + let d',_ = combine_env' man r fe f args fc es f_ask in (d', l) - let combine_assign ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in + let combine_assign man r fe f args fc es f_ask = + let (d,l) = man.local in (* No need to add1 here, already done in combine_env. *) if leq0 l then (d, l) else - let d',_ = combine_assign' ctx r fe f args fc es f_ask in + let d',_ = combine_assign' man r fe f args fc es f_ask in (d', l) - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | Queries.EvalFunvar e -> - let (d,l) = ctx.local in + let (d,l) = man.local in if leq0 l then Queries.AD.empty () else - query' ctx (Queries.EvalFunvar e) - | q -> query' ctx q + query' man (Queries.EvalFunvar e) + | q -> query' man q end @@ -379,33 +379,33 @@ struct let morphstate v (d,m) = S.morphstate v d, m - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) + let conv man = + { man with local = fst man.local + ; split = (fun d es -> man.split (d, snd man.local) es ) } - let context ctx fd (d,m) = S.context (conv ctx) fd d (* just the child analysis' context *) + let context man fd (d,m) = S.context (conv man) fd d (* just the child analysis' context *) - let lift_fun ctx f g = g (f (conv ctx)), snd ctx.local + let lift_fun man f g = g (f (conv man)), snd man.local - let sync ctx reason = lift_fun ctx S.sync ((|>) reason) - let query ctx = S.query (conv ctx) - let assign ctx lv e = lift_fun ctx S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx S.body ((|>) f) - let return ctx r f = lift_fun ctx S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx S.asm identity - let skip ctx = lift_fun ctx S.skip identity - let special ctx r f args = lift_fun ctx S.special ((|>) args % (|>) f % (|>) r) + let sync man reason = lift_fun man S.sync ((|>) reason) + let query man = S.query (conv man) + let assign man lv e = lift_fun man S.assign ((|>) e % (|>) lv) + let vdecl man v = lift_fun man S.vdecl ((|>) v) + let branch man e tv = lift_fun man S.branch ((|>) tv % (|>) e) + let body man f = lift_fun man S.body ((|>) f) + let return man r f = lift_fun man S.return ((|>) f % (|>) r) + let asm man = lift_fun man S.asm identity + let skip man = lift_fun man S.skip identity + let special man r f args = lift_fun man S.special ((|>) args % (|>) f % (|>) r) - let event ctx e octx = lift_fun ctx S.event ((|>) (conv octx) % (|>) e) + let event man e oman = lift_fun man S.event ((|>) (conv oman) % (|>) e) - let threadenter ctx ~multiple lval f args = S.threadenter (conv ctx) ~multiple lval f args |> List.map (fun d -> (d, snd ctx.local)) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) + let threadenter man ~multiple lval f args = S.threadenter (conv man) ~multiple lval f args |> List.map (fun d -> (d, snd man.local)) + let threadspawn man ~multiple lval f args fman = lift_fun man (S.threadspawn ~multiple) ((|>) (conv fman) % (|>) args % (|>) f % (|>) lval) - let enter ctx r f args = - let m = snd ctx.local in + let enter man r f args = + let m = snd man.local in let d' v_cur = if ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.context.widen" ~keepAttr:"widen" ~removeAttr:"no-widen" f then ( let v_old = M.find f.svar m in (* S.D.bot () if not found *) @@ -416,15 +416,15 @@ struct else v_cur, m in - S.enter (conv ctx) r f args + S.enter (conv man) r f args |> List.map (fun (c,v) -> (c,m), d' v) (* c: caller, v: callee *) - let paths_as_set ctx = - let m = snd ctx.local in - S.paths_as_set (conv ctx) |> List.map (fun v -> (v,m)) + let paths_as_set man = + let m = snd man.local in + S.paths_as_set (conv man) |> List.map (fun v -> (v,m)) - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) + let combine_env man r fe f args fc es f_ask = lift_fun man S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) + let combine_assign man r fe f args fc es f_ask = lift_fun man S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) end @@ -461,44 +461,44 @@ struct let morphstate v d = try `Lifted (S.morphstate v (D.unlift d)) with Deadcode -> d - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) + let conv man = + { man with local = D.unlift man.local + ; split = (fun d es -> man.split (D.lift d) es ) } - let context ctx fd = S.context (conv ctx) fd % D.unlift + let context man fd = S.context (conv man) fd % D.unlift - let lift_fun ctx f g h b = - try f @@ h (g (conv ctx)) + let lift_fun man f g h b = + try f @@ h (g (conv man)) with Deadcode -> b - let sync ctx reason = lift_fun ctx D.lift S.sync ((|>) reason) `Bot + let sync man reason = lift_fun man D.lift S.sync ((|>) reason) `Bot - let enter ctx r f args = + let enter man r f args = let liftmap = List.map (fun (x,y) -> D.lift x, D.lift y) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) [] + lift_fun man liftmap S.enter ((|>) args % (|>) f % (|>) r) [] - let paths_as_set ctx = + let paths_as_set man = let liftmap = List.map (fun x -> D.lift x) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) [D.bot ()] (* One dead path instead of none, such that combine_env gets called for functions with dead normal return (and thus longjmpy returns can be correctly handled by lifter). *) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun (x) -> x q) (Queries.Result.bot q) - let assign ctx lv e = lift_fun ctx D.lift S.assign ((|>) e % (|>) lv) `Bot - let vdecl ctx v = lift_fun ctx D.lift S.vdecl ((|>) v) `Bot - let branch ctx e tv = lift_fun ctx D.lift S.branch ((|>) tv % (|>) e) `Bot - let body ctx f = lift_fun ctx D.lift S.body ((|>) f) `Bot - let return ctx r f = lift_fun ctx D.lift S.return ((|>) f % (|>) r) `Bot - let asm ctx = lift_fun ctx D.lift S.asm identity `Bot - let skip ctx = lift_fun ctx D.lift S.skip identity `Bot - let special ctx r f args = lift_fun ctx D.lift S.special ((|>) args % (|>) f % (|>) r) `Bot - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx D.lift (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot - - let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot + lift_fun man liftmap S.paths_as_set (Fun.id) [D.bot ()] (* One dead path instead of none, such that combine_env gets called for functions with dead normal return (and thus longjmpy returns can be correctly handled by lifter). *) + + let query man (type a) (q: a Queries.t): a Queries.result = + lift_fun man identity S.query (fun (x) -> x q) (Queries.Result.bot q) + let assign man lv e = lift_fun man D.lift S.assign ((|>) e % (|>) lv) `Bot + let vdecl man v = lift_fun man D.lift S.vdecl ((|>) v) `Bot + let branch man e tv = lift_fun man D.lift S.branch ((|>) tv % (|>) e) `Bot + let body man f = lift_fun man D.lift S.body ((|>) f) `Bot + let return man r f = lift_fun man D.lift S.return ((|>) f % (|>) r) `Bot + let asm man = lift_fun man D.lift S.asm identity `Bot + let skip man = lift_fun man D.lift S.skip identity `Bot + let special man r f args = lift_fun man D.lift S.special ((|>) args % (|>) f % (|>) r) `Bot + let combine_env man r fe f args fc es f_ask = lift_fun man D.lift S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot + let combine_assign man r fe f args fc es f_ask = lift_fun man D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot + + let threadenter man ~multiple lval f args = lift_fun man (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] + let threadspawn man ~multiple lval f args fman = lift_fun man D.lift (S.threadspawn ~multiple) ((|>) (conv fman) % (|>) args % (|>) f % (|>) lval) `Bot + + let event (man:(D.t,G.t,C.t,V.t) man) (e:Events.t) (oman:(D.t,G.t,C.t,V.t) man):D.t = lift_fun man D.lift S.event ((|>) (conv oman) % (|>) e) `Bot end @@ -545,81 +545,81 @@ struct let startstate v = D.singleton (Spec.startstate v) let morphstate v d = D.map (Spec.morphstate v) d - let conv ctx x = - let rec ctx' = { ctx with ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx' q) + let conv man x = + let rec man' = { man with ask = (fun (type a) (q: a Queries.t) -> Spec.query man' q) ; local = x - ; split = (ctx.split % D.singleton) } + ; split = (man.split % D.singleton) } in - ctx' + man' - let context ctx fd l = + let context man fd l = if D.cardinal l <> 1 then failwith "PathSensitive2.context must be called with a singleton set." else let x = D.choose l in - Spec.context (conv ctx x) fd x + Spec.context (conv man x) fd x - let map ctx f g = + let map man f g = let h x xs = - try D.add (g (f (conv ctx x))) xs + try D.add (g (f (conv man x))) xs with Deadcode -> xs in - let d = D.fold h ctx.local (D.empty ()) in + let d = D.fold h man.local (D.empty ()) in if D.is_bot d then raise Deadcode else d - let fold' ctx f g h a = + let fold' man f g h a = let k x a = - try h a @@ g @@ f @@ conv ctx x + try h a @@ g @@ f @@ conv man x with Deadcode -> a in - D.fold k ctx.local a - - let assign ctx l e = map ctx Spec.assign (fun h -> h l e ) - let vdecl ctx v = map ctx Spec.vdecl (fun h -> h v) - let body ctx f = map ctx Spec.body (fun h -> h f ) - let return ctx e f = map ctx Spec.return (fun h -> h e f ) - let branch ctx e tv = map ctx Spec.branch (fun h -> h e tv) - let asm ctx = map ctx Spec.asm identity - let skip ctx = map ctx Spec.skip identity - let special ctx l f a = map ctx Spec.special (fun h -> h l f a) - - let event ctx e octx = - let fd1 = D.choose octx.local in - map ctx Spec.event (fun h -> h e (conv octx fd1)) - - let threadenter ctx ~multiple lval f args = + D.fold k man.local a + + let assign man l e = map man Spec.assign (fun h -> h l e ) + let vdecl man v = map man Spec.vdecl (fun h -> h v) + let body man f = map man Spec.body (fun h -> h f ) + let return man e f = map man Spec.return (fun h -> h e f ) + let branch man e tv = map man Spec.branch (fun h -> h e tv) + let asm man = map man Spec.asm identity + let skip man = map man Spec.skip identity + let special man l f a = map man Spec.special (fun h -> h l f a) + + let event man e oman = + let fd1 = D.choose oman.local in + map man Spec.event (fun h -> h e (conv oman fd1)) + + let threadenter man ~multiple lval f args = let g xs ys = (List.map (fun y -> D.singleton y) ys) @ xs in - fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] + fold' man (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] - let threadspawn ctx ~multiple lval f args fctx = - let fd1 = D.choose fctx.local in - map ctx (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fctx fd1)) + let threadspawn man ~multiple lval f args fman = + let fd1 = D.choose fman.local in + map man (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fman fd1)) - let sync ctx reason = map ctx Spec.sync (fun h -> h reason) + let sync man reason = map man Spec.sync (fun h -> h reason) - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = (* TODO: handle Invariant path like PathSensitive3? *) (* join results so that they are sound for all paths *) let module Result = (val Queries.Result.lattice q) in - fold' ctx Spec.query identity (fun x f -> Result.join x (f q)) (Result.bot ()) + fold' man Spec.query identity (fun x f -> Result.join x (f q)) (Result.bot ()) - let enter ctx l f a = + let enter man l f a = let g xs ys = (List.map (fun (x,y) -> D.singleton x, D.singleton y) ys) @ xs in - fold' ctx Spec.enter (fun h -> h l f a) g [] + fold' man Spec.enter (fun h -> h l f a) g [] - let paths_as_set ctx = + let paths_as_set man = (* Path-sensitivity is only here, not below! *) - let elems = D.elements ctx.local in + let elems = D.elements man.local in List.map (D.singleton) elems - let combine_env ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in + let combine_env man l fe f a fc d f_ask = + assert (D.cardinal man.local = 1); + let cd = D.choose man.local in let k x y = if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; try - let r = Spec.combine_env (conv ctx cd) l fe f a fc x f_ask in + let r = Spec.combine_env (conv man cd) l fe f a fc x f_ask in if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; D.add r y with Deadcode -> @@ -629,13 +629,13 @@ struct let d = D.fold k d (D.bot ()) in if D.is_bot d then raise Deadcode else d - let combine_assign ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in + let combine_assign man l fe f a fc d f_ask = + assert (D.cardinal man.local = 1); + let cd = D.choose man.local in let k x y = if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; try - let r = Spec.combine_assign (conv ctx cd) l fe f a fc x f_ask in + let r = Spec.combine_assign (conv man cd) l fe f a fc x f_ask in if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; D.add r y with Deadcode -> @@ -699,21 +699,21 @@ struct init marshal; AnalysisState.unsound_both_branches_dead := Some false - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); + let conv (man: (_, G.t, _, V.t) man): (_, S.G.t, _, S.V.t) man = + { man with + global = (fun v -> G.s (man.global (V.s v))); + sideg = (fun v g -> man.sideg (V.s v) (G.create_s g)); } - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | WarnGlobal g -> let g: V.t = Obj.obj g in begin match g with | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) + S.query (conv man) (WarnGlobal (Obj.repr g)) | `Right g -> - let em = G.node (ctx.global (V.node g)) in + let em = G.node (man.global (V.node g)) in EM.iter (fun exp tv -> match tv with | `Lifted tv -> @@ -733,7 +733,7 @@ struct let g: V.t = Obj.obj g in begin match g with | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) + S.query (conv man) (InvariantGlobal (Obj.repr g)) | `Right g -> Queries.Result.top q end @@ -741,14 +741,14 @@ struct let g: V.t = Obj.obj g in begin match g with | `Left g -> - S.query (conv ctx) (YamlEntryGlobal (Obj.repr g, task)) + S.query (conv man) (YamlEntryGlobal (Obj.repr g, task)) | `Right g -> Queries.Result.top q end | IterSysVars (vq, vf) -> (* vars for S *) let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); + S.query (conv man) (IterSysVars (vq, vf')); (* node vars for dead branches *) begin match vq with @@ -758,42 +758,42 @@ struct () end | _ -> - S.query (conv ctx) q + S.query (conv man) q - let branch ctx = S.branch (conv ctx) - let context ctx = S.context (conv ctx) + let branch man = S.branch (conv man) + let context man = S.context (conv man) - let branch ctx exp tv = + let branch man exp tv = if !AnalysisState.postsolving then ( try - let r = branch ctx exp tv in + let r = branch man exp tv in (* branch is live *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) + man.sideg (V.node man.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) r with Deadcode -> (* branch is dead *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) + man.sideg (V.node man.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) raise Deadcode ) else ( - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) - branch ctx exp tv + man.sideg (V.node man.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) + branch man exp tv ) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) + let assign man = S.assign (conv man) + let vdecl man = S.vdecl (conv man) + let enter man = S.enter (conv man) + let paths_as_set man = S.paths_as_set (conv man) + let body man = S.body (conv man) + let return man = S.return (conv man) + let combine_env man = S.combine_env (conv man) + let combine_assign man = S.combine_assign (conv man) + let special man = S.special (conv man) + let threadenter man = S.threadenter (conv man) + let threadspawn man ~multiple lv f args fman = S.threadspawn (conv man) ~multiple lv f args (conv fman) + let sync man = S.sync (conv man) + let skip man = S.skip (conv man) + let asm man = S.asm (conv man) + let event man e oman = S.event (conv man) e (conv oman) end diff --git a/src/lifters/wideningTokenLifter.ml b/src/lifters/wideningTokenLifter.ml index 634468a9ca..0ba069328a 100644 --- a/src/lifters/wideningTokenLifter.ml +++ b/src/lifters/wideningTokenLifter.ml @@ -125,25 +125,25 @@ struct let exitstate v = (S.exitstate v, TS.bot ()) let morphstate v (d, t) = (S.morphstate v d, t) - 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 keeps local widening tokens. *) - ; global = (fun g -> G.unlift (ctx.global g)) - ; sideg = (fun v g -> ctx.sideg v (g, !side_tokens)) (* Using side_tokens for side effect. *) + let conv (man: (D.t, G.t, C.t, V.t) man): (S.D.t, S.G.t, S.C.t, S.V.t) man = + { man with local = D.unlift man.local + ; split = (fun d es -> man.split (d, snd man.local) es) (* Split keeps local widening tokens. *) + ; global = (fun g -> G.unlift (man.global g)) + ; sideg = (fun v g -> man.sideg v (g, !side_tokens)) (* Using side_tokens for side effect. *) } - let context ctx fd = S.context (conv ctx) fd % D.unlift + let context man fd = S.context (conv man) fd % D.unlift let startcontext () = S.startcontext () - let lift_fun ctx f g h = - let new_tokens = ref (snd ctx.local) in (* New tokens not yet used during this transfer function, such that it is deterministic. *) + let lift_fun man f g h = + let new_tokens = ref (snd man.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; + local_tokens := snd man.local; let d = Fun.protect (fun () -> - h (g (conv ctx)) + h (g (conv man)) ) ~finally:(fun () -> local_tokens := old_local_tokens; add_ref := old_add @@ -156,30 +156,30 @@ struct let lift' d ts = (d, ts) - let paths_as_set ctx = + let paths_as_set man = let liftmap l ts = List.map (fun x -> (x, ts)) l in - lift_fun ctx liftmap S.paths_as_set (Fun.id) + lift_fun man liftmap S.paths_as_set (Fun.id) - let sync ctx reason = lift_fun ctx lift' S.sync ((|>) reason) + let sync man reason = lift_fun man lift' S.sync ((|>) reason) - let enter ctx r f args = + let enter man 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_env ctx r fe f args fc es f_ask = lift_fun ctx lift' S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) (* TODO: use tokens from es *) - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx lift' S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) (* TODO: use tokens from es *) - - let threadenter ctx ~multiple lval f args = lift_fun ctx (fun l ts -> List.map (Fun.flip lift' ts) l) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval ) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx lift' (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - let event ctx e octx = lift_fun ctx lift' S.event ((|>) (conv octx) % (|>) e) + lift_fun man liftmap S.enter ((|>) args % (|>) f % (|>) r) + + let query man (type a) (q: a Queries.t): a Queries.result = + lift_fun man Fun.const S.query (fun (x) -> x q) + let assign man lv e = lift_fun man lift' S.assign ((|>) e % (|>) lv) + let vdecl man v = lift_fun man lift' S.vdecl ((|>) v) + let branch man e tv = lift_fun man lift' S.branch ((|>) tv % (|>) e) + let body man f = lift_fun man lift' S.body ((|>) f) + let return man r f = lift_fun man lift' S.return ((|>) f % (|>) r) + let asm man = lift_fun man lift' S.asm identity + let skip man = lift_fun man lift' S.skip identity + let special man r f args = lift_fun man lift' S.special ((|>) args % (|>) f % (|>) r) + let combine_env man r fe f args fc es f_ask = lift_fun man lift' S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) (* TODO: use tokens from es *) + let combine_assign man r fe f args fc es f_ask = lift_fun man lift' S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) (* TODO: use tokens from es *) + + let threadenter man ~multiple lval f args = lift_fun man (fun l ts -> List.map (Fun.flip lift' ts) l) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval ) + let threadspawn man ~multiple lval f args fman = lift_fun man lift' (S.threadspawn ~multiple) ((|>) (conv fman) % (|>) args % (|>) f % (|>) lval) + let event man e oman = lift_fun man lift' S.event ((|>) (conv oman) % (|>) e) end diff --git a/src/witness/observerAnalysis.ml b/src/witness/observerAnalysis.ml index 490a51021a..533d1c5d58 100644 --- a/src/witness/observerAnalysis.ml +++ b/src/witness/observerAnalysis.ml @@ -44,40 +44,40 @@ struct end | _ -> d - let step_ctx ctx = step ctx.local ctx.prev_node ctx.node + let step_man man = step man.local man.prev_node man.node (* transfer functions *) - let assign ctx (lval:lval) (rval:exp) : D.t = - step_ctx ctx + let assign man (lval:lval) (rval:exp) : D.t = + step_man man - let vdecl ctx (_:varinfo) : D.t = - step_ctx ctx + let vdecl man (_:varinfo) : D.t = + step_man man - let branch ctx (exp:exp) (tv:bool) : D.t = - step_ctx ctx + let branch man (exp:exp) (tv:bool) : D.t = + step_man man - let body ctx (f:fundec) : D.t = - step_ctx ctx + let body man (f:fundec) : D.t = + step_man man - let return ctx (exp:exp option) (f:fundec) : D.t = - step_ctx ctx + let return man (exp:exp option) (f:fundec) : D.t = + step_man man - let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = - (* ctx.local doesn't matter here? *) - [ctx.local, step ctx.local ctx.prev_node (FunctionEntry f)] + let enter man (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list = + (* man.local doesn't matter here? *) + [man.local, step man.local man.prev_node (FunctionEntry f)] - let combine_env ctx lval fexp f args fc au f_ask = - ctx.local (* Don't yet consider call edge done before assign. *) + let combine_env man lval fexp f args fc au f_ask = + man.local (* Don't yet consider call edge done before assign. *) - let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = - step au (Function f) ctx.node (* Consider call edge done after entire call-assign. *) + let combine_assign man (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask: Queries.ask) : D.t = + step au (Function f) man.node (* Consider call edge done after entire call-assign. *) - let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = - step_ctx ctx + let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = + step_man man let startstate v = `Lifted Automaton.initial - let threadenter ctx ~multiple lval f args = [D.top ()] - let threadspawn ctx ~multiple lval f args fctx = ctx.local + let threadenter man ~multiple lval f args = [D.top ()] + let threadspawn man ~multiple lval f args fman = man.local let exitstate v = D.top () end diff --git a/src/witness/witnessConstraints.ml b/src/witness/witnessConstraints.ml index e361dbaa54..dc492fdabb 100644 --- a/src/witness/witnessConstraints.ml +++ b/src/witness/witnessConstraints.ml @@ -117,111 +117,111 @@ struct | exception Not_found -> M.debug ~category:Witness ~tags:[Category Analyzer] "PathSensitive3 sync predecessor not found"; R.bot () - let step_ctx ctx x e = + let step_man man x e = try - step ctx.prev_node (ctx.context ()) x e (snd ctx.local) - with Ctx_failure _ -> + step man.prev_node (man.context ()) x e (snd man.local) + with Man_failure _ -> R.bot () - let step_ctx_edge ctx x = step_ctx ctx x (CFGEdge ctx.edge) - let step_ctx_inlined_edge ctx x = step_ctx ctx x (InlinedEdge ctx.edge) + let step_man_edge man x = step_man man x (CFGEdge man.edge) + let step_man_inlined_edge man x = step_man man x (InlinedEdge man.edge) let nosync x = Sync.singleton x (SyncSet.singleton x) - let conv ctx x = - let rec ctx' = - { ctx with + let conv man x = + let rec man' = + { man with local = x; - ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx' q); + ask = (fun (type a) (q: a Queries.t) -> Spec.query man' q); split; } and split y es = - let yr = step_ctx_edge ctx x in - ctx.split (Dom.singleton y yr, Sync.bot ()) es + let yr = step_man_edge man x in + man.split (Dom.singleton y yr, Sync.bot ()) es in - ctx' + man' - let context ctx fd (l, _) = + let context man fd (l, _) = if Dom.cardinal l <> 1 then failwith "PathSensitive3.context must be called with a singleton set." else let x = Dom.choose_key l in - Spec.context (conv ctx x) fd @@ x + Spec.context (conv man x) fd @@ x let startcontext = Spec.startcontext - let map ctx f g = + let map man f g = (* we now use Sync for every tf such that threadspawn after tf could look up state before tf *) let h x (xs, sync) = try - let x' = g (f (conv ctx x)) in - (Dom.add x' (step_ctx_edge ctx x) xs, Sync.add x' (SyncSet.singleton x) sync) + let x' = g (f (conv man x)) in + (Dom.add x' (step_man_edge man x) xs, Sync.add x' (SyncSet.singleton x) sync) with Deadcode -> (xs, sync) in - let d = Dom.fold_keys h (fst ctx.local) (Dom.empty (), Sync.bot ()) in + let d = Dom.fold_keys h (fst man.local) (Dom.empty (), Sync.bot ()) in if Dom.is_bot (fst d) then raise Deadcode else d (* TODO???? *) - let map_event ctx e = + let map_event man e = (* we now use Sync for every tf such that threadspawn after tf could look up state before tf *) let h x (xs, sync) = try - let x' = Spec.event (conv ctx x) e (conv ctx x) in - (Dom.add x' (step_ctx_edge ctx x) xs, Sync.add x' (SyncSet.singleton x) sync) + let x' = Spec.event (conv man x) e (conv man x) in + (Dom.add x' (step_man_edge man x) xs, Sync.add x' (SyncSet.singleton x) sync) with Deadcode -> (xs, sync) in - let d = Dom.fold_keys h (fst ctx.local) (Dom.empty (), Sync.bot ()) in + let d = Dom.fold_keys h (fst man.local) (Dom.empty (), Sync.bot ()) in if Dom.is_bot (fst d) then raise Deadcode else d - let fold' ctx f g h a = + let fold' man f g h a = let k x a = - try h a x @@ g @@ f @@ conv ctx x + try h a x @@ g @@ f @@ conv man x with Deadcode -> a in - Dom.fold_keys k (fst ctx.local) a + Dom.fold_keys k (fst man.local) a - let fold'' ctx f g h a = + let fold'' man f g h a = let k x r a = - try h a x r @@ g @@ f @@ conv ctx x + try h a x r @@ g @@ f @@ conv man x with Deadcode -> a in - Dom.fold k (fst ctx.local) a - - let assign ctx l e = map ctx Spec.assign (fun h -> h l e ) - let vdecl ctx v = map ctx Spec.vdecl (fun h -> h v) - let body ctx f = map ctx Spec.body (fun h -> h f ) - let return ctx e f = map ctx Spec.return (fun h -> h e f ) - let branch ctx e tv = map ctx Spec.branch (fun h -> h e tv) - let asm ctx = map ctx Spec.asm identity - let skip ctx = map ctx Spec.skip identity - let special ctx l f a = map ctx Spec.special (fun h -> h l f a) - let event ctx e octx = map_event ctx e (* TODO: ???? *) - - let paths_as_set ctx = - let (a,b) = ctx.local in + Dom.fold k (fst man.local) a + + let assign man l e = map man Spec.assign (fun h -> h l e ) + let vdecl man v = map man Spec.vdecl (fun h -> h v) + let body man f = map man Spec.body (fun h -> h f ) + let return man e f = map man Spec.return (fun h -> h e f ) + let branch man e tv = map man Spec.branch (fun h -> h e tv) + let asm man = map man Spec.asm identity + let skip man = map man Spec.skip identity + let special man l f a = map man Spec.special (fun h -> h l f a) + let event man e oman = map_event man e (* TODO: ???? *) + + let paths_as_set man = + let (a,b) = man.local in let r = Dom.bindings a in List.map (fun (x,v) -> (Dom.singleton x v, b)) r - let threadenter ctx ~multiple lval f args = + let threadenter man ~multiple lval f args = let g xs x' ys = let ys' = List.map (fun y -> - let yr = step ctx.prev_node (ctx.context ()) x' (ThreadEntry (lval, f, args)) (nosync x') in (* threadenter called on before-sync state *) + let yr = step man.prev_node (man.context ()) x' (ThreadEntry (lval, f, args)) (nosync x') in (* threadenter called on before-sync state *) (Dom.singleton y yr, Sync.bot ()) ) ys in ys' @ xs in - fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] - let threadspawn ctx ~multiple lval f args fctx = - let fd1 = Dom.choose_key (fst fctx.local) in - map ctx (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fctx fd1)) + fold' man (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] + let threadspawn man ~multiple lval f args fman = + let fd1 = Dom.choose_key (fst fman.local) in + map man (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fman fd1)) - let sync ctx reason = - fold'' ctx Spec.sync (fun h -> h reason) (fun (a, async) x r a' -> + let sync man reason = + fold'' man Spec.sync (fun h -> h reason) (fun (a, async) x r a' -> (Dom.add a' r a, Sync.add a' (SyncSet.singleton x) async) ) (Dom.empty (), Sync.bot ()) - let query ctx (type a) (q: a Queries.t): a Queries.result = + let query man (type a) (q: a Queries.t): a Queries.result = match q with | Queries.IterPrevVars f -> if M.tracing then M.tracei "witness" "IterPrevVars"; @@ -234,35 +234,35 @@ struct f (I.to_int x) (n, Obj.repr c, I.to_int j) e ) r; if M.tracing then M.traceu "witness" "" (* unindent! *) - ) (fst ctx.local); + ) (fst man.local); (* check that sync mappings don't leak into solution (except Function) *) (* TODO: disabled because we now use and leave Sync for every tf, such that threadspawn after tf could look up state before tf *) - (* begin match ctx.node with + (* begin match man.node with | Function _ -> () (* returns post-sync in FromSpec *) - | _ -> assert (Sync.is_bot (snd ctx.local)); + | _ -> assert (Sync.is_bot (snd man.local)); end; *) if M.tracing then M.traceu "witness" ""; () | Queries.IterVars f -> Dom.iter (fun x r -> f (I.to_int x) - ) (fst ctx.local); + ) (fst man.local); () | Queries.PathQuery (i, q) -> (* TODO: optimize indexing, using inner hashcons somehow? *) (* let (d, _) = List.at (S.elements s) i in *) - let (d, _) = List.find (fun (x, _) -> I.to_int x = i) (Dom.bindings (fst ctx.local)) in - Spec.query (conv ctx d) q + let (d, _) = List.find (fun (x, _) -> I.to_int x = i) (Dom.bindings (fst man.local)) in + Spec.query (conv man d) q | Queries.Invariant ({path=Some i; _} as c) -> (* TODO: optimize indexing, using inner hashcons somehow? *) (* let (d, _) = List.at (S.elements s) i in *) - let (d, _) = List.find (fun (x, _) -> I.to_int x = i) (Dom.bindings (fst ctx.local)) in - Spec.query (conv ctx d) (Invariant c) + let (d, _) = List.find (fun (x, _) -> I.to_int x = i) (Dom.bindings (fst man.local)) in + Spec.query (conv man d) (Invariant c) | _ -> (* join results so that they are sound for all paths *) let module Result = (val Queries.Result.lattice q) in - fold' ctx Spec.query identity (fun x _ f -> Result.join x (f q)) (Result.bot ()) + fold' man Spec.query identity (fun x _ f -> Result.join x (f q)) (Result.bot ()) let should_inline f = (* (* inline __VERIFIER_error because Control requires the corresponding FunctionEntry node *) @@ -270,20 +270,20 @@ struct (* TODO: don't inline __VERIFIER functions for CPAchecker, but inlining needed for WP *) true - let enter ctx l f a = + let enter man l f a = let g xs x' ys = let ys' = List.map (fun (x,y) -> (* R.bot () isn't right here? doesn't actually matter? *) let yr = if should_inline f then - step_ctx ctx x' (InlineEntry (l, f, a)) + step_man man x' (InlineEntry (l, f, a)) else R.bot () in (* keep left syncs so combine gets them for no-inline case *) (* must lookup and short-circuit because enter may modify first in pair (e.g. abortUnless) *) let syncs = - match Sync.find x' (snd ctx.local) with + match Sync.find x' (snd man.local) with | syncs -> syncs | exception Not_found -> M.debug ~category:Witness ~tags:[Category Analyzer] "PathSensitive3 sync predecessor not found"; @@ -294,37 +294,37 @@ struct in ys' @ xs in - fold' ctx Spec.enter (fun h -> h l f a) g [] + fold' man Spec.enter (fun h -> h l f a) g [] - let combine_env ctx l fe f a fc d f_ask = + let combine_env man l fe f a fc d f_ask = (* Don't yet consider call edge done before assign. *) - assert (Dom.cardinal (fst ctx.local) = 1); - let (cd, cdr) = Dom.choose (fst ctx.local) in + assert (Dom.cardinal (fst man.local) = 1); + let (cd, cdr) = Dom.choose (fst man.local) in let k x (y, sync) = try - let x' = Spec.combine_env (conv ctx cd) l fe f a fc x f_ask in - (Dom.add x' cdr y, Sync.add x' (Sync.find cd (snd ctx.local)) sync) (* keep predecessors and sync from ctx, sync required for step_ctx_inlined_edge in combine_assign *) + let x' = Spec.combine_env (conv man cd) l fe f a fc x f_ask in + (Dom.add x' cdr y, Sync.add x' (Sync.find cd (snd man.local)) sync) (* keep predecessors and sync from man, sync required for step_man_inlined_edge in combine_assign *) with Deadcode -> (y, sync) in let d = Dom.fold_keys k (fst d) (Dom.bot (), Sync.bot ()) in if Dom.is_bot (fst d) then raise Deadcode else d - let combine_assign ctx l fe f a fc d f_ask = + let combine_assign man l fe f a fc d f_ask = (* Consider call edge done after entire call-assign. *) - assert (Dom.cardinal (fst ctx.local) = 1); - let cd = Dom.choose_key (fst ctx.local) in + assert (Dom.cardinal (fst man.local) = 1); + let cd = Dom.choose_key (fst man.local) in let k x (y, sync) = let r = if should_inline f then (* returns already post-sync in FromSpec *) let returnr = step (Function f) (Option.get fc) x (InlineReturn (l, f, a)) (nosync x) in (* fc should be Some outside of MCP *) - let procr = step_ctx_inlined_edge ctx cd in + let procr = step_man_inlined_edge man cd in R.join procr returnr else - step_ctx_edge ctx cd + step_man_edge man cd in try - let x' = Spec.combine_assign (conv ctx cd) l fe f a fc x f_ask in + let x' = Spec.combine_assign (conv man cd) l fe f a fc x f_ask in (Dom.add x' r y, Sync.add x' (SyncSet.singleton x) sync) with Deadcode -> (y, sync) in diff --git a/src/witness/z3/violationZ3.z3.ml b/src/witness/z3/violationZ3.z3.ml index d004320ad3..53ac756d17 100644 --- a/src/witness/z3/violationZ3.z3.ml +++ b/src/witness/z3/violationZ3.z3.ml @@ -16,7 +16,7 @@ struct (* ("smt.core.minimize", "true"); *) (* ("sat.core.minimize", "true"); *) ] - let ctx = mk_context cfg + let man = mk_context cfg type var = varinfo @@ -40,43 +40,43 @@ struct let get_const m x = match VarMap.find_opt x m with | Some x -> x - | None -> Arithmetic.Integer.mk_const_s ctx (get_name x) - let sort = Arithmetic.Integer.mk_sort ctx + | None -> Arithmetic.Integer.mk_const_s man (get_name x) + let sort = Arithmetic.Integer.mk_sort man let freshen env x = - VarMap.add x (Expr.mk_fresh_const ctx (get_name x) sort) env + VarMap.add x (Expr.mk_fresh_const man (get_name x) sort) env end let bool_to_int expr = - Boolean.mk_ite ctx expr (Arithmetic.Integer.mk_numeral_i ctx 1) (Arithmetic.Integer.mk_numeral_i ctx 0) + Boolean.mk_ite man expr (Arithmetic.Integer.mk_numeral_i man 1) (Arithmetic.Integer.mk_numeral_i man 0) let int_to_bool expr = - Boolean.mk_distinct ctx [expr; Arithmetic.Integer.mk_numeral_i ctx 0] + Boolean.mk_distinct man [expr; Arithmetic.Integer.mk_numeral_i man 0] let rec exp_to_expr env = function | Const (CInt (i, _, _)) -> - Arithmetic.Integer.mk_numeral_s ctx (Z.to_string i) + Arithmetic.Integer.mk_numeral_s man (Z.to_string i) | Lval (Var v, NoOffset) -> Env.get_const env v | BinOp (PlusA, e1, e2, TInt _) -> - Arithmetic.mk_add ctx [exp_to_expr env e1; exp_to_expr env e2] + Arithmetic.mk_add man [exp_to_expr env e1; exp_to_expr env e2] | BinOp (MinusA, e1, e2, TInt _) -> - Arithmetic.mk_sub ctx [exp_to_expr env e1; exp_to_expr env e2] + Arithmetic.mk_sub man [exp_to_expr env e1; exp_to_expr env e2] | BinOp (Mult, e1, e2, TInt _) -> - Arithmetic.mk_mul ctx [exp_to_expr env e1; exp_to_expr env e2] + Arithmetic.mk_mul man [exp_to_expr env e1; exp_to_expr env e2] | BinOp (Eq, e1, e2, TInt _) -> - bool_to_int (Boolean.mk_eq ctx (exp_to_expr env e1) (exp_to_expr env e2)) + bool_to_int (Boolean.mk_eq man (exp_to_expr env e1) (exp_to_expr env e2)) | BinOp (Ne, e1, e2, TInt _) -> - bool_to_int (Boolean.mk_distinct ctx [exp_to_expr env e1; exp_to_expr env e2]) + bool_to_int (Boolean.mk_distinct man [exp_to_expr env e1; exp_to_expr env e2]) | BinOp (Gt, e1, e2, TInt _) -> - bool_to_int (Arithmetic.mk_gt ctx (exp_to_expr env e1) (exp_to_expr env e2)) + bool_to_int (Arithmetic.mk_gt man (exp_to_expr env e1) (exp_to_expr env e2)) | BinOp (Lt, e1, e2, TInt _) -> - bool_to_int (Arithmetic.mk_lt ctx (exp_to_expr env e1) (exp_to_expr env e2)) + bool_to_int (Arithmetic.mk_lt man (exp_to_expr env e1) (exp_to_expr env e2)) | BinOp (Ge, e1, e2, TInt _) -> - bool_to_int (Arithmetic.mk_ge ctx (exp_to_expr env e1) (exp_to_expr env e2)) + bool_to_int (Arithmetic.mk_ge man (exp_to_expr env e1) (exp_to_expr env e2)) | BinOp (Le, e1, e2, TInt _) -> - bool_to_int (Arithmetic.mk_le ctx (exp_to_expr env e1) (exp_to_expr env e2)) + bool_to_int (Arithmetic.mk_le man (exp_to_expr env e1) (exp_to_expr env e2)) | UnOp (LNot, e, TInt _) -> - bool_to_int (Boolean.mk_not ctx (int_to_bool (exp_to_expr env e))) + bool_to_int (Boolean.mk_not man (int_to_bool (exp_to_expr env e))) | e -> failwith @@ GobPretty.sprintf "exp_to_expr: %a" Cil.d_exp e @@ -86,11 +86,11 @@ struct let wp_assert env (from_node, (edge: MyARG.inline_edge), _) = match edge with | MyARG.CFGEdge (MyCFG.Assign ((Var v, NoOffset), e)) -> let env' = Env.freshen env v in - (env', [Boolean.mk_eq ctx (Env.get_const env v) (exp_to_expr env' e)]) + (env', [Boolean.mk_eq man (Env.get_const env v) (exp_to_expr env' e)]) | MyARG.CFGEdge (MyCFG.Test (e, true)) -> - (env, [Boolean.mk_distinct ctx [exp_to_expr env e; Arithmetic.Integer.mk_numeral_i ctx 0]]) + (env, [Boolean.mk_distinct man [exp_to_expr env e; Arithmetic.Integer.mk_numeral_i man 0]]) | MyARG.CFGEdge (MyCFG.Test (e, false)) -> - (env, [Boolean.mk_eq ctx (exp_to_expr env e) (Arithmetic.Integer.mk_numeral_i ctx 0)]) + (env, [Boolean.mk_eq man (exp_to_expr env e) (Arithmetic.Integer.mk_numeral_i man 0)]) | MyARG.CFGEdge (MyCFG.Entry fd) -> let env' = List.fold_left (fun acc formal -> Env.freshen acc formal @@ -98,7 +98,7 @@ struct in let eqs = List.mapi (fun i formal -> let arg_vname = get_arg_vname i in - Boolean.mk_eq ctx (Env.get_const env formal) (Env.get_const env' arg_vname) + Boolean.mk_eq man (Env.get_const env formal) (Env.get_const env' arg_vname) ) fd.sformals in (env', eqs) @@ -110,7 +110,7 @@ struct in let eqs = List.mapi (fun i arg -> let arg_vname = get_arg_vname i in - Boolean.mk_eq ctx (Env.get_const env arg_vname) (exp_to_expr env' arg) + Boolean.mk_eq man (Env.get_const env arg_vname) (exp_to_expr env' arg) ) args in (env', eqs) @@ -118,14 +118,14 @@ struct (env, []) | MyARG.CFGEdge (MyCFG.Ret (Some e, fd)) -> let env' = Env.freshen env return_vname in - (env', [Boolean.mk_eq ctx (Env.get_const env return_vname) (exp_to_expr env' e)]) + (env', [Boolean.mk_eq man (Env.get_const env return_vname) (exp_to_expr env' e)]) | MyARG.InlineReturn (None, _, _) -> (env, []) | MyARG.InlineReturn (Some (Var v, NoOffset), _, _) -> let env' = Env.freshen env v in - (env', [Boolean.mk_eq ctx (Env.get_const env v) (Env.get_const env' return_vname)]) + (env', [Boolean.mk_eq man (Env.get_const env v) (Env.get_const env' return_vname)]) | _ -> - (* (env, Boolean.mk_true ctx) *) + (* (env, Boolean.mk_true man) *) failwith @@ GobPretty.sprintf "wp_assert: %a" MyARG.pretty_inline_edge edge let const_get_symbol (expr: Expr.expr): Symbol.symbol = @@ -140,7 +140,7 @@ struct | Unknown let wp_path path = - let solver = Solver.mk_simple_solver ctx in + let solver = Solver.mk_simple_solver man in let rec iter_wp revpath i env = match revpath with | [] -> Feasible | step :: revpath' -> @@ -149,14 +149,14 @@ struct | [] -> iter_wp revpath' (i - 1) env' | [expr] -> do_assert revpath' i env' expr | exprs -> - let expr = Boolean.mk_and ctx exprs in + let expr = Boolean.mk_and man exprs in do_assert revpath' i env' expr end and do_assert revpath' i env' expr = Logs.debug "%d: %s" i (Expr.to_string expr); - let track_const = Boolean.mk_const ctx (Symbol.mk_int ctx i) in + let track_const = Boolean.mk_const man (Symbol.mk_int man i) in Solver.assert_and_track solver expr track_const; let status = Solver.check solver [] in From 197e804746da4f58d31574ecee2736a8fd5962da Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 18 Dec 2024 15:14:29 +0100 Subject: [PATCH 560/566] Ignore rename --- .git-blame-ignore-revs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index d0b623a8e4..0aa6cec831 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -37,3 +37,6 @@ c3e2cc848479ae86de5542b6ab0e75a74e9cf8c9 # Fix LibraryFunctions.invalidate_actions indentation 5662024232f32fe74dd25c9317dee4436ecb212d + +# Rename ctx -> man +0c155e68607fede6fab17704a9a7aee38df5408e From e559cb62e113ee6b3d62969b6c7268931433eb6a Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Wed, 18 Dec 2024 15:22:29 +0100 Subject: [PATCH 561/566] Brainfart --- src/analyses/mCP.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 0420931ffd..b972195bad 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -83,11 +83,11 @@ struct check_deps !activated; activated := topo_sort_an !activated; begin - match get_string_list "ana.man_sens" with - | [] -> (* use values of "ana.man_insens" (blacklist) *) - let cont_inse = map' find_id @@ get_string_list "ana.man_insens" in + match get_string_list "ana.ctx_sens" with + | [] -> (* use values of "ana.ctx_insens" (blacklist) *) + let cont_inse = map' find_id @@ get_string_list "ana.ctx_insens" in activated_context_sens := List.filter (fun (n, _) -> not (List.mem n cont_inse)) !activated; - | sens -> (* use values of "ana.man_sens" (whitelist) *) + | sens -> (* use values of "ana.ctx_sens" (whitelist) *) let cont_sens = map' find_id @@ sens in activated_context_sens := List.filter (fun (n, _) -> List.mem n cont_sens) !activated; end; From 1b8d70ac5f1d97d78681929a8442bceec265fcfd Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 19 Dec 2024 10:00:59 +0100 Subject: [PATCH 562/566] extractPthread: Change accidentally changed name back --- src/analyses/extractPthread.ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/analyses/extractPthread.ml b/src/analyses/extractPthread.ml index cd3fce184d..d31243ae70 100644 --- a/src/analyses/extractPthread.ml +++ b/src/analyses/extractPthread.ml @@ -233,15 +233,15 @@ let promela_main : fun_name = "mainfun" (* assign tid: promela_main -> 0 *) let _ = Tbls.ThreadTidTbl.get promela_main -let fun_ctx man f = - let man_hash = - match PthreadDomain.Ctx.to_int man with +let fun_ctx ctx f = + let ctx_hash = + match PthreadDomain.Ctx.to_int ctx with | Some i -> i |> i64_to_int |> Tbls.CtxTbl.get |> string_of_int | None -> "TOP" in - f.vname ^ "_" ^ man_hash + f.vname ^ "_" ^ ctx_hash module Tasks = SetDomain.Make (Lattice.Prod (Queries.AD) (PthreadDomain.D)) From 196bf69699086405534e6284577f3679a597c3b9 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 19 Dec 2024 10:01:38 +0100 Subject: [PATCH 563/566] Fix `sync` timing call Co-authored-by: Simmo Saan --- src/analyses/basePriv.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index a8696a1532..0caf45f08d 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -1802,7 +1802,7 @@ struct let write_global ?invariant ask getg sideg st x v = time "write_global" (Priv.write_global ?invariant ask getg sideg st x) v let lock ask getg cpa m = time "lock" (Priv.lock ask getg cpa) m let unlock ask getg sideg st m = time "unlock" (Priv.unlock ask getg sideg st) m - let sync reason man = time "sync" (Priv.sync reason) man + let sync ask getg sideg st reason = time "sync" (Priv.sync ask getg sideg st) reason let escape ask getg sideg st escaped = time "escape" (Priv.escape ask getg sideg st) escaped let enter_multithreaded ask getg sideg st = time "enter_multithreaded" (Priv.enter_multithreaded ask getg sideg) st let threadenter ask st = time "threadenter" (Priv.threadenter ask) st From 72110456b2add644df9cc6e1412876123053d744 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 19 Dec 2024 10:02:51 +0100 Subject: [PATCH 564/566] Z3: Change accidentally changed things back to `ctx` --- src/witness/z3/violationZ3.z3.ml | 56 ++++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/witness/z3/violationZ3.z3.ml b/src/witness/z3/violationZ3.z3.ml index 53ac756d17..d004320ad3 100644 --- a/src/witness/z3/violationZ3.z3.ml +++ b/src/witness/z3/violationZ3.z3.ml @@ -16,7 +16,7 @@ struct (* ("smt.core.minimize", "true"); *) (* ("sat.core.minimize", "true"); *) ] - let man = mk_context cfg + let ctx = mk_context cfg type var = varinfo @@ -40,43 +40,43 @@ struct let get_const m x = match VarMap.find_opt x m with | Some x -> x - | None -> Arithmetic.Integer.mk_const_s man (get_name x) - let sort = Arithmetic.Integer.mk_sort man + | None -> Arithmetic.Integer.mk_const_s ctx (get_name x) + let sort = Arithmetic.Integer.mk_sort ctx let freshen env x = - VarMap.add x (Expr.mk_fresh_const man (get_name x) sort) env + VarMap.add x (Expr.mk_fresh_const ctx (get_name x) sort) env end let bool_to_int expr = - Boolean.mk_ite man expr (Arithmetic.Integer.mk_numeral_i man 1) (Arithmetic.Integer.mk_numeral_i man 0) + Boolean.mk_ite ctx expr (Arithmetic.Integer.mk_numeral_i ctx 1) (Arithmetic.Integer.mk_numeral_i ctx 0) let int_to_bool expr = - Boolean.mk_distinct man [expr; Arithmetic.Integer.mk_numeral_i man 0] + Boolean.mk_distinct ctx [expr; Arithmetic.Integer.mk_numeral_i ctx 0] let rec exp_to_expr env = function | Const (CInt (i, _, _)) -> - Arithmetic.Integer.mk_numeral_s man (Z.to_string i) + Arithmetic.Integer.mk_numeral_s ctx (Z.to_string i) | Lval (Var v, NoOffset) -> Env.get_const env v | BinOp (PlusA, e1, e2, TInt _) -> - Arithmetic.mk_add man [exp_to_expr env e1; exp_to_expr env e2] + Arithmetic.mk_add ctx [exp_to_expr env e1; exp_to_expr env e2] | BinOp (MinusA, e1, e2, TInt _) -> - Arithmetic.mk_sub man [exp_to_expr env e1; exp_to_expr env e2] + Arithmetic.mk_sub ctx [exp_to_expr env e1; exp_to_expr env e2] | BinOp (Mult, e1, e2, TInt _) -> - Arithmetic.mk_mul man [exp_to_expr env e1; exp_to_expr env e2] + Arithmetic.mk_mul ctx [exp_to_expr env e1; exp_to_expr env e2] | BinOp (Eq, e1, e2, TInt _) -> - bool_to_int (Boolean.mk_eq man (exp_to_expr env e1) (exp_to_expr env e2)) + bool_to_int (Boolean.mk_eq ctx (exp_to_expr env e1) (exp_to_expr env e2)) | BinOp (Ne, e1, e2, TInt _) -> - bool_to_int (Boolean.mk_distinct man [exp_to_expr env e1; exp_to_expr env e2]) + bool_to_int (Boolean.mk_distinct ctx [exp_to_expr env e1; exp_to_expr env e2]) | BinOp (Gt, e1, e2, TInt _) -> - bool_to_int (Arithmetic.mk_gt man (exp_to_expr env e1) (exp_to_expr env e2)) + bool_to_int (Arithmetic.mk_gt ctx (exp_to_expr env e1) (exp_to_expr env e2)) | BinOp (Lt, e1, e2, TInt _) -> - bool_to_int (Arithmetic.mk_lt man (exp_to_expr env e1) (exp_to_expr env e2)) + bool_to_int (Arithmetic.mk_lt ctx (exp_to_expr env e1) (exp_to_expr env e2)) | BinOp (Ge, e1, e2, TInt _) -> - bool_to_int (Arithmetic.mk_ge man (exp_to_expr env e1) (exp_to_expr env e2)) + bool_to_int (Arithmetic.mk_ge ctx (exp_to_expr env e1) (exp_to_expr env e2)) | BinOp (Le, e1, e2, TInt _) -> - bool_to_int (Arithmetic.mk_le man (exp_to_expr env e1) (exp_to_expr env e2)) + bool_to_int (Arithmetic.mk_le ctx (exp_to_expr env e1) (exp_to_expr env e2)) | UnOp (LNot, e, TInt _) -> - bool_to_int (Boolean.mk_not man (int_to_bool (exp_to_expr env e))) + bool_to_int (Boolean.mk_not ctx (int_to_bool (exp_to_expr env e))) | e -> failwith @@ GobPretty.sprintf "exp_to_expr: %a" Cil.d_exp e @@ -86,11 +86,11 @@ struct let wp_assert env (from_node, (edge: MyARG.inline_edge), _) = match edge with | MyARG.CFGEdge (MyCFG.Assign ((Var v, NoOffset), e)) -> let env' = Env.freshen env v in - (env', [Boolean.mk_eq man (Env.get_const env v) (exp_to_expr env' e)]) + (env', [Boolean.mk_eq ctx (Env.get_const env v) (exp_to_expr env' e)]) | MyARG.CFGEdge (MyCFG.Test (e, true)) -> - (env, [Boolean.mk_distinct man [exp_to_expr env e; Arithmetic.Integer.mk_numeral_i man 0]]) + (env, [Boolean.mk_distinct ctx [exp_to_expr env e; Arithmetic.Integer.mk_numeral_i ctx 0]]) | MyARG.CFGEdge (MyCFG.Test (e, false)) -> - (env, [Boolean.mk_eq man (exp_to_expr env e) (Arithmetic.Integer.mk_numeral_i man 0)]) + (env, [Boolean.mk_eq ctx (exp_to_expr env e) (Arithmetic.Integer.mk_numeral_i ctx 0)]) | MyARG.CFGEdge (MyCFG.Entry fd) -> let env' = List.fold_left (fun acc formal -> Env.freshen acc formal @@ -98,7 +98,7 @@ struct in let eqs = List.mapi (fun i formal -> let arg_vname = get_arg_vname i in - Boolean.mk_eq man (Env.get_const env formal) (Env.get_const env' arg_vname) + Boolean.mk_eq ctx (Env.get_const env formal) (Env.get_const env' arg_vname) ) fd.sformals in (env', eqs) @@ -110,7 +110,7 @@ struct in let eqs = List.mapi (fun i arg -> let arg_vname = get_arg_vname i in - Boolean.mk_eq man (Env.get_const env arg_vname) (exp_to_expr env' arg) + Boolean.mk_eq ctx (Env.get_const env arg_vname) (exp_to_expr env' arg) ) args in (env', eqs) @@ -118,14 +118,14 @@ struct (env, []) | MyARG.CFGEdge (MyCFG.Ret (Some e, fd)) -> let env' = Env.freshen env return_vname in - (env', [Boolean.mk_eq man (Env.get_const env return_vname) (exp_to_expr env' e)]) + (env', [Boolean.mk_eq ctx (Env.get_const env return_vname) (exp_to_expr env' e)]) | MyARG.InlineReturn (None, _, _) -> (env, []) | MyARG.InlineReturn (Some (Var v, NoOffset), _, _) -> let env' = Env.freshen env v in - (env', [Boolean.mk_eq man (Env.get_const env v) (Env.get_const env' return_vname)]) + (env', [Boolean.mk_eq ctx (Env.get_const env v) (Env.get_const env' return_vname)]) | _ -> - (* (env, Boolean.mk_true man) *) + (* (env, Boolean.mk_true ctx) *) failwith @@ GobPretty.sprintf "wp_assert: %a" MyARG.pretty_inline_edge edge let const_get_symbol (expr: Expr.expr): Symbol.symbol = @@ -140,7 +140,7 @@ struct | Unknown let wp_path path = - let solver = Solver.mk_simple_solver man in + let solver = Solver.mk_simple_solver ctx in let rec iter_wp revpath i env = match revpath with | [] -> Feasible | step :: revpath' -> @@ -149,14 +149,14 @@ struct | [] -> iter_wp revpath' (i - 1) env' | [expr] -> do_assert revpath' i env' expr | exprs -> - let expr = Boolean.mk_and man exprs in + let expr = Boolean.mk_and ctx exprs in do_assert revpath' i env' expr end and do_assert revpath' i env' expr = Logs.debug "%d: %s" i (Expr.to_string expr); - let track_const = Boolean.mk_const man (Symbol.mk_int man i) in + let track_const = Boolean.mk_const ctx (Symbol.mk_int ctx i) in Solver.assert_and_track solver expr track_const; let status = Solver.check solver [] in From aa87b3e8fd4d39ab9d77f3eaed716a8b68402ee6 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 19 Dec 2024 10:06:14 +0100 Subject: [PATCH 565/566] Re-indent combine_assign --- src/analyses/region.ml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/analyses/region.ml b/src/analyses/region.ml index 7cc8d34111..0fb61059e2 100644 --- a/src/analyses/region.ml +++ b/src/analyses/region.ml @@ -143,14 +143,14 @@ struct match au with | `Lifted reg -> begin let old_regpart = man.global () in - let regpart, reg = match lval with - | None -> (old_regpart, reg) - | Some lval -> Reg.assign lval (AddrOf (ReturnUtil.return_lval ())) (old_regpart, reg) - in - let regpart, reg = Reg.remove_vars [ReturnUtil.return_varinfo ()] (regpart, reg) in - if not (RegPart.leq regpart old_regpart) then - man.sideg () regpart; - `Lifted reg + let regpart, reg = match lval with + | None -> (old_regpart, reg) + | Some lval -> Reg.assign lval (AddrOf (ReturnUtil.return_lval ())) (old_regpart, reg) + in + let regpart, reg = Reg.remove_vars [ReturnUtil.return_varinfo ()] (regpart, reg) in + if not (RegPart.leq regpart old_regpart) then + man.sideg () regpart; + `Lifted reg end | _ -> au From 24861856805fde2426b763ac50600d1df75b17aa Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Thu, 19 Dec 2024 10:11:00 +0100 Subject: [PATCH 566/566] Doc comment for `man` --- src/framework/analyses.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 985f013ede..5890fef402 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -142,7 +142,8 @@ struct | `Lifted x -> LD.printXml f x end - +(** Man(ager) is passed to transfer functions and offers access to various facilities, e.g., to access the local state, the context, + read values from globals, side-effect values to globals and trigger events. *) type ('d,'g,'c,'v) man = { ask : 'a. 'a Queries.t -> 'a Queries.result (* Inlined Queries.ask *) ; emit : Events.t -> unit