From 2bc950b41a13fad70b31c1b12bd112a76699ac36 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 5 Oct 2022 14:50:46 +0300 Subject: [PATCH 001/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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/248] 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 9b4b255f29f887b5cffb38280e7ebf86ed7cdd4c Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 27 May 2024 17:45:58 +0200 Subject: [PATCH 030/248] 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 031/248] 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 682eb0c183c897a31c8862c95a05bbc416437eb9 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 13:15:22 +0300 Subject: [PATCH 032/248] 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 aeae2a97d2d475bba55ed2f0b8af94d497e7ab1c Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 4 Jun 2024 16:37:20 +0300 Subject: [PATCH 033/248] 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 034/248] 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 035/248] 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 a3b436284771cea0e1d86bda1a5b32da12f5e849 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 13 Jun 2024 18:04:33 +0300 Subject: [PATCH 036/248] 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 037/248] 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 e286a48decb04641f2e9bbe7fa1b96d1ba845293 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 14 Jun 2024 13:50:46 +0300 Subject: [PATCH 038/248] 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 039/248] 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 040/248] 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 041/248] 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 042/248] 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 043/248] 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 044/248] 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 62f01a9778ccc61b3894a292a522ee397394ab5f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 18 Jun 2024 18:05:57 +0300 Subject: [PATCH 045/248] 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 046/248] 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 047/248] 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 048/248] 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 049/248] 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 050/248] 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 051/248] 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 052/248] 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 053/248] 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 054/248] 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 055/248] 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 af781ed684c538cfa98ef0d35a9d258fec62e495 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 20 Jun 2024 12:56:40 +0300 Subject: [PATCH 056/248] 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 057/248] 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 058/248] 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 059/248] 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 060/248] 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 01d275829fa8c532cbf44ab3a439f04bd3886e19 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 21 Jun 2024 15:19:14 +0300 Subject: [PATCH 061/248] 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 56ed7b5589da639d5ec9028c8558cb75810dada1 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 26 Jun 2024 17:05:20 +0300 Subject: [PATCH 062/248] 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 063/248] 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 064/248] 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 065/248] 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 066/248] 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 067/248] 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 8971af4da76b730fb3fe0eea624cd2a551561d44 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 16 Jul 2024 17:59:53 +0300 Subject: [PATCH 068/248] 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 069/248] 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 070/248] 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 071/248] 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 072/248] 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 726725b88015547e9aeab38232fe130ab113993f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 17 Jul 2024 11:15:45 +0300 Subject: [PATCH 073/248] 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 074/248] 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 075/248] 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 0f99b6652ada5e5c3863e80dcb6c8d746bcd4f3f Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 18 Jul 2024 20:06:08 +0300 Subject: [PATCH 076/248] 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 077/248] 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 078/248] 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 079/248] 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 080/248] 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 081/248] 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 72713a62ad3a3e09f57ec6b8a70e16cfa09ff76a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 24 Jul 2024 13:31:06 +0300 Subject: [PATCH 082/248] 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 40b34346ff77e078a29dbaef2d195fe552402e86 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 30 Jul 2024 11:04:28 +0300 Subject: [PATCH 083/248] 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 084/248] 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 085/248] 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 086/248] 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 10a63d0acc1248d1399eb2ed397b3253db185002 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Wed, 31 Jul 2024 12:41:00 +0300 Subject: [PATCH 087/248] 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 088/248] 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 089/248] 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 090/248] 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 091/248] 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 092/248] 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 093/248] 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 094/248] 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 095/248] 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 096/248] 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 097/248] 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 098/248] 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 e630b744c71f370a746ef1b9955f11efa654523a Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 12 Aug 2024 14:20:48 +0300 Subject: [PATCH 099/248] 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 100/248] 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 101/248] 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 102/248] 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 103/248] 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 3c54188f07c43816d016e95da96255a57e135e39 Mon Sep 17 00:00:00 2001 From: Karoliine Holter Date: Thu, 22 Aug 2024 16:15:29 +0300 Subject: [PATCH 104/248] 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 b148e207ed7df3b65727ec504db3cf1e90442258 Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Fri, 20 Sep 2024 17:11:37 +0200 Subject: [PATCH 105/248] 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 106/248] 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 107/248] 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 108/248] 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 109/248] 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 110/248] 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 111/248] 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 112/248] 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 113/248] 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 114/248] 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 115/248] 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 116/248] 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 117/248] 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 118/248] 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 119/248] 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 120/248] 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 82d580f5214fec7dfac8b9fa5bf2576181837998 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Tue, 24 Sep 2024 11:38:32 +0300 Subject: [PATCH 121/248] 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 122/248] 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 123/248] 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 124/248] 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 125/248] 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 126/248] 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 127/248] 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 128/248] 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 129/248] 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 130/248] 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 131/248] 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 132/248] 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 133/248] 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 134/248] 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 135/248] 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 136/248] 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 137/248] 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 138/248] 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 139/248] 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 140/248] 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 141/248] 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 142/248] 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 143/248] 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 144/248] 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 145/248] 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 146/248] 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 147/248] 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 148/248] 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 149/248] 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 150/248] 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 151/248] 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 152/248] 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 153/248] 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 154/248] 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 155/248] 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 156/248] 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 157/248] 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 158/248] 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 159/248] 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 160/248] 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 161/248] 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 162/248] 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 163/248] 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 164/248] 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 165/248] 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 166/248] 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 167/248] 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 168/248] 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 169/248] 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 170/248] 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 171/248] 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 172/248] 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 173/248] 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 174/248] 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 175/248] 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 176/248] 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 177/248] 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 178/248] 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 179/248] 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 180/248] 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 181/248] 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 182/248] 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 183/248] 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 184/248] 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 185/248] 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 186/248] 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 187/248] 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 188/248] 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 189/248] 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 190/248] 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 191/248] 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 192/248] 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 193/248] 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 194/248] 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 195/248] 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 196/248] 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 197/248] 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 198/248] 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 199/248] 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 200/248] 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 201/248] 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 202/248] 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 203/248] 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 3970a2fde041afba272b4a93e3a7c4a73f05cdca Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Mon, 28 Oct 2024 13:48:12 +0200 Subject: [PATCH 204/248] 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 205/248] 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 206/248] 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 207/248] 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 208/248] 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 209/248] 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 210/248] 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 211/248] 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 212/248] 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 213/248] 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 214/248] 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 215/248] 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 216/248] 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 217/248] 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 218/248] 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 219/248] 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 220/248] 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 1bb50df301ddfd63b9a15677c3bf6f9cf5d026ff Mon Sep 17 00:00:00 2001 From: Michael Schwarz Date: Mon, 4 Nov 2024 10:38:13 +0100 Subject: [PATCH 221/248] 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 222/248] 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 223/248] 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 224/248] 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 225/248] 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 226/248] 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 227/248] 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 228/248] 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 229/248] 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 230/248] 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 231/248] 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 232/248] 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 233/248] 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 8d8b6752af3d23260a9c5ab080eb26bdf740006d Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 14 Nov 2024 17:40:49 +0200 Subject: [PATCH 234/248] 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 235/248] 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 236/248] 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 237/248] 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 aeb2376811f30d6d9b7f814b685d04643ede5190 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Thu, 21 Nov 2024 15:31:39 +0200 Subject: [PATCH 238/248] 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 0ca1bb30f50d12bec84198ae404994c510e37431 Mon Sep 17 00:00:00 2001 From: Simmo Saan Date: Fri, 22 Nov 2024 11:11:34 +0200 Subject: [PATCH 239/248] 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 240/248] 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 241/248] 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 242/248] 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 243/248] 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 244/248] 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 245/248] 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 246/248] 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 247/248] 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 248/248] 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