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 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 420cc7145e..1fb07a7dc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## v2.5.0 +Functionally equivalent to Goblint in SV-COMP 2025. + +* 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). * Add linear two-variable equalities analysis (#1297, #1412, #1466). @@ -10,7 +21,7 @@ * 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). diff --git a/conf/svcomp-ghost.json b/conf/svcomp-ghost.json new file mode 100644 index 0000000000..d1b4171b2b --- /dev/null +++ b/conf/svcomp-ghost.json @@ -0,0 +1,139 @@ +{ + "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", + "mutexGhosts", + "pthreadMutexType" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "memLeak", + "threadflag" + ], + "context": { + "widen": false + }, + "base": { + "arrays": { + "domain": "partitioned" + }, + "invariant": { + "local": false, + "global": true + } + }, + "relation": { + "invariant": { + "local": false, + "global": true, + "one-var": false + } + }, + "apron": { + "invariant": { + "diff-box": true + } + }, + "var_eq": { + "invariant": { + "enabled": false + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "octagon", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification", + "noOverflows", + "termination", + "tmpSpecialAnalysis" + ] + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": false + }, + "yaml": { + "enabled": true, + "format-version": "2.1", + "entry-types": [ + "flow_insensitive_invariant", + "ghost_instrumentation" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": true, + "other": true, + "accessed": false, + "exact": true, + "all-locals": false, + "flow_insensitive-as": "invariant_set-location_invariant" + } + }, + "pre": { + "enabled": false + } +} diff --git a/conf/svcomp-validate.json b/conf/svcomp-validate.json index 8e11fee7f5..bec171f1e8 100644 --- a/conf/svcomp-validate.json +++ b/conf/svcomp-validate.json @@ -10,7 +10,8 @@ "interval": true }, "float": { - "interval": true + "interval": true, + "evaluate_math_functions": true }, "activated": [ "base", @@ -67,6 +68,7 @@ "wideningThresholds", "loopUnrollHeuristic", "memsafetySpecification", + "noOverflows", "termination", "tmpSpecialAnalysis" ] diff --git a/conf/svcomp.json b/conf/svcomp.json index 50136def50..dedc393ba1 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", @@ -66,6 +67,7 @@ "wideningThresholds", "loopUnrollHeuristic", "memsafetySpecification", + "noOverflows", "termination", "tmpSpecialAnalysis" ] 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", diff --git a/conf/svcomp25-validate.json b/conf/svcomp25-validate.json new file mode 100644 index 0000000000..bec171f1e8 --- /dev/null +++ b/conf/svcomp25-validate.json @@ -0,0 +1,123 @@ +{ + "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", + "noOverflows", + "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..dedc393ba1 --- /dev/null +++ b/conf/svcomp25.json @@ -0,0 +1,118 @@ +{ + "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", + "noOverflows", + "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 + } +} 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: diff --git a/dune-project b/dune-project index 54915cf964..9a1d958484 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 @@ -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 44e5ccd2c2..219c67d011 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" @@ -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, 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 9fbee1e02b..e5176b9007 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" @@ -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"} @@ -139,8 +139,8 @@ post-messages: [ ] pin-depends: [ [ - "goblint-cil.2.0.4" - "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" + "goblint-cil.2.0.5" + "git+https://github.com/goblint/cil.git#c79208b21ea61d7b72eae29a18c1ddeda4795dfd" ] [ "camlidl.1.12" diff --git a/goblint.opam.template b/goblint.opam.template index 0a517fbfa0..84dcc24d8d 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, 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/gobview b/gobview index 76e42c34d3..f8ae450f2a 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 76e42c34d36bd2ab6900efd661a972ba4824f065 +Subproject commit f8ae450f2ac83f5ccf0ee174d2b7ae8376bb4f76 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) diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index eee7b218c5..90537e57fe 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -54,6 +54,7 @@ "EnumsDomain", # included in IntDomain "CongruenceDomain", # included in IntDomain "IntDomTuple", # included in IntDomain + "WitnessGhostVar", # included in WitnessGhost "ConfigVersion", "ConfigProfile", 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 \ diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index da14dfff1d..df3cf545c5 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -634,6 +634,15 @@ struct ) |> Enum.fold (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none + let query_invariant_global ctx g = + if GobConfig.get_bool "ana.relation.invariant.global" && ctx.ask (GhostVarAvailable Multithreaded) then ( + let var = WitnessGhost.to_varinfo Multithreaded in + let inv = Priv.invariant_global (Analyses.ask_of_ctx ctx) ctx.global g in + Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) + else + Invariant.none + let query ctx (type a) (q: a Queries.t): a Queries.result = let open Queries in let st = ctx.local in @@ -655,6 +664,9 @@ struct let vf' x = vf (Obj.repr x) in Priv.iter_sys_vars ctx.global vq vf' | Queries.Invariant context -> query_invariant ctx context + | Queries.InvariantGlobal g -> + let g: V.t = Obj.obj g in + query_invariant_global ctx g | _ -> Result.top q @@ -690,7 +702,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 @@ -701,7 +713,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 +749,7 @@ struct (* TODO: parallel write_global? *) let st = - WideningTokens.with_side_tokens (WideningTokens.TS.of_list uuids) (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 +783,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/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 15df394d54..257bec24d8 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -46,6 +46,9 @@ module type S = val thread_return: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> ThreadIdDomain.Thread.t -> relation_components_t -> relation_components_t val iter_sys_vars: (V.t -> G.t) -> VarQuery.t -> V.t VarQuery.f -> unit (** [Queries.IterSysVars] for apron. *) + val invariant_global: Q.ask -> (V.t -> G.t) -> V.t -> Invariant.t + (** Returns flow-insensitive invariant for global unknown. *) + val invariant_vars: Q.ask -> (V.t -> G.t) -> relation_components_t -> varinfo list (** Returns global variables which are privatized. *) @@ -137,6 +140,7 @@ struct {rel = RD.top (); priv = startstate ()} let iter_sys_vars getg vq vf = () + let invariant_global ask getg g = Invariant.none let invariant_vars ask getg st = [] let init () = () @@ -424,6 +428,7 @@ struct {rel = getg (); priv = startstate ()} let iter_sys_vars getg vq vf = () (* TODO: or report singleton global for any Global query? *) + let invariant_global ask getg g = Invariant.none let invariant_vars ask getg st = protected_vars ask (* TODO: is this right? *) let finalize () = () @@ -708,6 +713,41 @@ struct let init () = () let finalize () = () + + let invariant_global (ask: Q.ask) (getg: V.t -> G.t): V.t -> Invariant.t = function + | `Left m' -> (* mutex *) + let atomic = LockDomain.MustLock.equal m' (LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var) in + if atomic || ask.f (GhostVarAvailable (Locked m')) then ( + (* filters like query_invariant *) + let one_var = GobConfig.get_bool "ana.relation.invariant.one-var" in + let exact = GobConfig.get_bool "witness.invariant.exact" in + + let rel = keep_only_protected_globals ask m' (get_m_with_mutex_inits ask getg m') in (* Could be more precise if mutex_inits invariant is added by disjunction instead of joining abstract values. *) + let inv = + RD.invariant rel + |> List.enum + |> Enum.filter_map (fun (lincons1: Apron.Lincons1.t) -> + (* filter one-vars and exact *) + (* TODO: exact filtering doesn't really work with octagon because it returns two SUPEQ constraints instead *) + if (one_var || GobApron.Lincons1.num_vars lincons1 >= 2) && (exact || Apron.Lincons1.get_typ lincons1 <> EQ) then + RD.cil_exp_of_lincons1 lincons1 + |> Option.filter (fun exp -> not (InvariantCil.exp_contains_tmp exp)) + else + None + ) + |> Enum.fold (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none + in + if atomic then + inv + else ( + let var = WitnessGhost.to_varinfo (Locked m') in + Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) + ) + else + Invariant.none + | g -> (* global *) + Invariant.none (* Could output unprotected one-variable (so non-relational) invariants, but probably not very useful. [BasePriv] does those anyway. *) end (** May written variables. *) @@ -1275,6 +1315,8 @@ struct | _ -> () let finalize () = () + + let invariant_global ask getg g = Invariant.none end module TracingPriv = functor (Priv: S) -> functor (RD: RelationDomain.RD) -> diff --git a/src/analyses/base.ml b/src/analyses/base.ml index cea2c8bcee..f6e41361a3 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -445,7 +445,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 @@ -1181,6 +1181,9 @@ struct not is_alloc || (is_alloc && not (ctx.ask (Queries.IsHeapVar v))) let query_invariant ctx context = + let keep_local = GobConfig.get_bool "ana.base.invariant.local" in + let keep_global = GobConfig.get_bool "ana.base.invariant.global" in + let cpa = ctx.local.BaseDomain.cpa in let ask = Analyses.ask_of_ctx ctx in @@ -1193,6 +1196,13 @@ struct in let module I = ValueDomain.ValueInvariant (Arg) in + let var_filter v = + if is_global ask v then + keep_global + else + keep_local + in + let var_invariant ?offset v = if not (InvariantCil.var_is_heap v) then I.key_invariant v ?offset (Arg.find v) @@ -1203,14 +1213,23 @@ struct if Lval.Set.is_top context.Invariant.lvals then ( if !earlyglobs || ThreadFlag.has_ever_been_multi ask then ( let cpa_invariant = - CPA.fold (fun k v a -> - if not (is_global ask k) then - Invariant.(a && var_invariant k) - else - a - ) cpa Invariant.none + if keep_local then ( + CPA.fold (fun k v a -> + if not (is_global ask k) then + Invariant.(a && var_invariant k) + else + a + ) cpa Invariant.none + ) + else + Invariant.none + in + let priv_vars = + if keep_global then + Priv.invariant_vars ask (priv_getg ctx.global) ctx.local + else + [] in - let priv_vars = Priv.invariant_vars ask (priv_getg ctx.global) ctx.local in let priv_invariant = List.fold_left (fun acc v -> Invariant.(var_invariant v && acc) @@ -1220,7 +1239,10 @@ struct ) else ( CPA.fold (fun k v a -> - Invariant.(a && var_invariant k) + if var_filter k then + Invariant.(a && var_invariant k) + else + a ) cpa Invariant.none ) ) @@ -1228,7 +1250,7 @@ struct Lval.Set.fold (fun k a -> let i = match k with - | (Var v, offset) when not (InvariantCil.var_is_heap v) -> + | (Var v, offset) when var_filter v && not (InvariantCil.var_is_heap v) -> (try I.key_invariant_lval v ~offset ~lval:k (Arg.find v) with Not_found -> Invariant.none) | _ -> Invariant.none in @@ -1243,13 +1265,23 @@ struct Invariant.none let query_invariant_global ctx g = - if GobConfig.get_bool "ana.base.invariant.enabled" && get_bool "exp.earlyglobs" then ( + if GobConfig.get_bool "ana.base.invariant.enabled" && GobConfig.get_bool "ana.base.invariant.global" then ( (* Currently these global invariants are only sound with earlyglobs enabled for both single- and multi-threaded programs. - Otherwise, the values of globals in single-threaded mode are not accounted for. *) - (* TODO: account for single-threaded values without earlyglobs. *) + Otherwise, the values of globals in single-threaded mode are not accounted for. + They are also made sound without earlyglobs using the multithreaded mode ghost variable. *) match g with | `Left g' -> (* priv *) - Priv.invariant_global (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) g' + let inv = Priv.invariant_global (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) g' in + if get_bool "exp.earlyglobs" then + inv + else ( + if ctx.ask (GhostVarAvailable Multithreaded) then ( + let var = WitnessGhost.to_varinfo Multithreaded in + Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) + else + Invariant.none + ) | `Right _ -> (* thread return *) Invariant.none ) @@ -2651,6 +2683,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 -> @@ -3051,7 +3092,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 @@ -3070,7 +3111,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 @@ -3084,8 +3125,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/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 *) diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index 946b8f8cc5..3afd758daa 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -250,7 +250,7 @@ struct let invariant_global ask getg = function | `Right g' -> (* global *) - ValueDomain.invariant_global (read_unprotected_global getg) g' + ValueDomain.invariant_global (read_unprotected_global getg) g' (* Could be more precise if mutex_inits invariant is added by disjunction instead of joining abstract values. *) | _ -> (* mutex *) Invariant.none @@ -339,6 +339,31 @@ module PerMutexMeetPrivBase = struct include PerMutexPrivBase + let invariant_global (ask: Q.ask) getg = function + | `Left m' -> (* mutex *) + let atomic = LockDomain.MustLock.equal m' (LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var) in + if atomic || ask.f (GhostVarAvailable (Locked m')) then ( + let cpa = get_m_with_mutex_inits ask getg m' in (* Could be more precise if mutex_inits invariant is added by disjunction instead of joining abstract values. *) + let inv = CPA.fold (fun v _ acc -> + if ask.f (MustBeProtectedBy {mutex = m'; global = v; write = true; protection = Strong}) then + let inv = ValueDomain.invariant_global (fun g -> CPA.find g cpa) v in + Invariant.(acc && inv) + else + acc + ) cpa Invariant.none + in + if atomic then + inv + else ( + let var = WitnessGhost.to_varinfo (Locked m') in + Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) + ) + else + Invariant.none + | g -> (* global *) + invariant_global ask getg g + let invariant_vars ask getg (st: _ BaseDomain.basecomponents_t) = (* Mutex-meet local states contain precisely the protected global variables, so we can do fewer queries than {!protected_vars}. *) @@ -374,8 +399,11 @@ struct in if not invariant then ( if M.tracing then M.tracel "priv" "WRITE GLOBAL SIDE %a = %a" CilType.Varinfo.pretty x VD.pretty v; - sideg (V.global x) (CPA.singleton x v) - (* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write. *) + let side_cpa = CPA.singleton x v in + sideg (V.global x) side_cpa; + if !earlyglobs && not (ThreadFlag.is_currently_multi ask) then + sideg V.mutex_inits side_cpa (* Also side to inits because with earlyglobs enter_multithreaded does not side everything to inits *) + (* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write. *) ); {st with cpa = cpa'} (* let write_global ask getg sideg cpa x v = @@ -676,7 +704,7 @@ struct let invariant_global ask getg = function | `Middle g -> (* global *) - ValueDomain.invariant_global (read_unprotected_global getg) g + ValueDomain.invariant_global (read_unprotected_global getg) g (* Could be more precise if mutex_inits invariant is added by disjunction instead of joining abstract values. *) | `Left _ | `Right _ -> (* mutex or thread *) Invariant.none @@ -749,8 +777,8 @@ struct if not invariant then ( if not (Param.handle_atomic && ask.f MustBeAtomic) then sideg (V.unprotected x) v; (* Delay publishing unprotected write in the atomic section. *) - if !earlyglobs then (* earlyglobs workaround for 13/60 *) - sideg (V.protected x) v + if !earlyglobs && not (ThreadFlag.is_currently_multi ask) then (* earlyglobs workaround for 13/60 *) + sideg (V.protected x) v (* Also side to protected because with earlyglobs enter_multithreaded does not side everything to protected *) (* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write since W is implicit. *) ); if Param.handle_atomic && ask.f MustBeAtomic then @@ -851,13 +879,37 @@ struct vf (V.protected g); | _ -> () - let invariant_global ask getg g = + let invariant_global (ask: Q.ask) getg g = let getg = Wrapper.getg ask getg in match g with | `Left g' -> (* unprotected *) ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g' - | `Right g -> (* protected *) - Invariant.none + | `Right g' -> (* protected *) + let locks = ask.f (Q.MustProtectingLocks g') in + if LockDomain.MustLockset.is_all locks || LockDomain.MustLockset.is_empty locks then + Invariant.none + else if VD.equal (getg (V.protected g')) (getg (V.unprotected g')) then + Invariant.none (* don't output protected invariant because it's the same as unprotected *) + else ( + (* Only read g' as protected, everything else (e.g. pointed to variables) may be unprotected. + See 56-witness/69-ghost-ptr-protection and https://github.com/goblint/analyzer/pull/1394#discussion_r1698136411. *) + let read_global g = getg (if CilType.Varinfo.equal g g' then V.protected g else V.unprotected g) in + let inv = ValueDomain.invariant_global read_global g' in + (* Very conservative about multiple (write-)protecting mutexes: invariant is not claimed when any of them is held. + It should be possible to be more precise because writes only happen with all of them held, + but conjunction is unsound when one of the mutexes is temporarily unlocked. + Hypothetical read-protection is also somehow relevant. *) + LockDomain.MustLockset.fold (fun m acc -> + if LockDomain.MustLock.equal m (LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var) then + acc + else if ask.f (GhostVarAvailable (Locked m)) then ( + let var = WitnessGhost.to_varinfo (Locked m) in + Invariant.(of_exp (Lval (GoblintCil.var var)) || acc) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) + else + Invariant.none + ) locks inv + ) let invariant_vars ask getg st = protected_vars ask end @@ -998,7 +1050,7 @@ struct (* sync: M -> (2^M -> (G -> D)) *) include AbstractLockCenteredBase (ThreadMap) (LockCenteredBase.CPA) - let global_init_thread = RichVarinfo.single ~name:"global_init" + let global_init_thread = RichVarinfo.single ~name:"global_init" ~typ:GoblintCil.voidType let current_thread (ask: Q.ask): Thread.t = if !AnalysisState.global_initialization then ThreadIdDomain.Thread.threadinit (global_init_thread ()) ~multiple:false @@ -1040,11 +1092,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 +1150,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)); @@ -1902,6 +1954,17 @@ struct if M.tracing then M.traceu "priv" "-> %a" BaseComponents.pretty r; r + let invariant_global ask getg g = + if M.tracing then M.traceli "priv" "invariant_global %a" V.pretty g; + let getg x = + let r = getg x in + if M.tracing then M.trace "priv" "getg %a -> %a" V.pretty x G.pretty r; + r + in + let r = invariant_global ask getg g in + if M.tracing then M.traceu "priv" "-> %a" Invariant.pretty r; + r + end let priv_module: (module S) Lazy.t = diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index 6212b6de90..f2f36b1360 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 @@ -303,6 +303,10 @@ struct (* InvariantGlobal is special: it only goes to corresponding analysis and the argument variant is unlifted for it *) let (n, g): V.t = Obj.obj g in f ~q:(InvariantGlobal (Obj.repr g)) (Result.top ()) (n, spec n, assoc n ctx.local) + | Queries.YamlEntryGlobal (g, task) -> + (* YamlEntryGlobal is special: it only goes to corresponding analysis and the argument variant is unlifted for it *) + let (n, g): V.t = Obj.obj g in + f ~q:(YamlEntryGlobal (Obj.repr g, task)) (Result.top ()) (n, spec n, assoc n ctx.local) | Queries.PartAccess a -> Obj.repr (access ctx a) | Queries.IterSysVars (vq, fi) -> @@ -355,7 +359,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/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index 9b6aa4f4ca..3710d2db9c 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -121,14 +121,17 @@ struct let add ctx ((addr, rw): AddrRW.t): D.t = match addr with - | Addr mv -> + | Addr ((v, o) as mv) -> let (s, m) = ctx.local in let s' = MustLocksetRW.add_mval_rw (mv, rw) s in let m' = - if MutexTypeAnalysis.must_be_recursive ctx mv then - MustMultiplicity.increment mv m - else + match ctx.ask (Queries.MutexType (v, Offset.Unit.of_offs o)) with + | `Lifted Recursive -> MustMultiplicity.increment mv m + | `Lifted NonRec -> + if MustLocksetRW.mem_mval mv s then + M.error ~category:M.Category.Behavior.Undefined.double_locking "Acquiring a non-recursive mutex that is already held"; m + | `Bot | `Top -> m in (s', m') | NullPtr -> @@ -223,6 +226,8 @@ struct true else *) MustLockset.mem ml protecting + | Queries.MustProtectingLocks g -> + protecting ~write:true Strong g | Queries.MustLockset -> let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in held_locks diff --git a/src/analyses/mutexGhosts.ml b/src/analyses/mutexGhosts.ml new file mode 100644 index 0000000000..3deec3ef59 --- /dev/null +++ b/src/analyses/mutexGhosts.ml @@ -0,0 +1,191 @@ +(** Analysis for generating ghost variables corresponding to mutexes ([mutexGhosts]). *) + +open Analyses + +module NodeSet = Queries.NS + + +module Spec = +struct + include UnitAnalysis.Spec + let name () = "mutexGhosts" + + module V = + struct + include Printable.Either3 (Node) (LockDomain.MustLock) (BoolDomain.Bool) + let node x = `Left x + let lock x = `Middle x + let threadcreate = `Right false + let update = `Right true + let is_write_only = function + | `Left _ -> false + | `Middle _ -> true + | `Right false -> false + | `Right true -> true + end + + module Locked = + struct + include LockDomain.MayLocksetNoRW + let name () = "locked" + end + module Unlocked = + struct + include LockDomain.MayLocksetNoRW + let name () = "unlocked" + end + module MultiThread = + struct + include BoolDomain.MayBool + let name () = "multithread" + end + module G = + struct + include Lattice.Lift2 (Lattice.Prod3 (Locked) (Unlocked) (MultiThread)) (Lattice.Lift2 (BoolDomain.MayBool) (NodeSet)) + let node = function + | `Bot -> (Locked.bot (), Unlocked.bot (), MultiThread.bot ()) + | `Lifted1 x -> x + | _ -> failwith "MutexGhosts.node" + let lock = function + | `Bot -> BoolDomain.MayBool.bot () + | `Lifted2 (`Lifted1 x) -> x + | _ -> failwith "MutexGhosts.lock" + let threadcreate = function + | `Bot -> NodeSet.bot () + | `Lifted2 (`Lifted2 x) -> x + | _ -> failwith "MutexGhosts.threadcreate" + let update = threadcreate + let create_node node = `Lifted1 node + let create_lock lock = `Lifted2 (`Lifted1 lock) + let create_threadcreate threadcreate = `Lifted2 (`Lifted2 threadcreate) + let create_update = create_threadcreate + end + + let mustlock_of_addr (addr: LockDomain.Addr.t): LockDomain.MustLock.t option = + match addr with + | Addr mv when LockDomain.Mval.is_definite mv -> Some (LockDomain.MustLock.of_mval mv) + | _ -> None + + let event ctx e octx = + let verifier_atomic_addr = LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var in + begin match e with + | Events.Lock (l, _) when not (LockDomain.Addr.equal l verifier_atomic_addr) -> + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.singleton l, Unlocked.bot (), MultiThread.bot ())); + if !AnalysisState.postsolving then ( + ctx.sideg V.update (G.create_update (NodeSet.singleton ctx.prev_node)); + let (locked, _, _) = G.node (ctx.global (V.node ctx.prev_node)) in + if Locked.cardinal locked > 1 then ( + Locked.iter (fun lock -> + Option.iter (fun lock -> + ctx.sideg (V.lock lock) (G.create_lock true) + ) (mustlock_of_addr lock) + ) locked + ); + ) + | Events.Unlock l when not (LockDomain.Addr.equal l verifier_atomic_addr) -> + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.singleton l, MultiThread.bot ())); + if !AnalysisState.postsolving then ( + ctx.sideg V.update (G.create_update (NodeSet.singleton ctx.prev_node)); + let (_, unlocked, _) = G.node (ctx.global (V.node ctx.prev_node)) in + if Locked.cardinal unlocked > 1 then ( + Locked.iter (fun lock -> + Option.iter (fun lock -> + ctx.sideg (V.lock lock) (G.create_lock true) + ) (mustlock_of_addr lock) + ) unlocked + ); + ) + | Events.EnterMultiThreaded -> + ctx.sideg (V.node ctx.prev_node) (G.create_node (Locked.bot (), Unlocked.bot (), true)); + if !AnalysisState.postsolving then + ctx.sideg V.update (G.create_update (NodeSet.singleton ctx.prev_node)); + | _ -> () + end; + ctx.local + + let threadspawn ctx ~multiple lval f args octx = + ctx.sideg V.threadcreate (G.create_threadcreate (NodeSet.singleton ctx.node)); + ctx.local + + let ghost_var_available ctx = function + | WitnessGhost.Var.Locked ((v, o) as lock) -> not (Offset.Z.contains_index o) && not (G.lock (ctx.global (V.lock lock))) + | Multithreaded -> true + + let ghost_var_available ctx v = + WitnessGhost.enabled () && ghost_var_available ctx v + + module VariableSet = Set.Make (YamlWitnessType.GhostInstrumentation.Variable) + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | GhostVarAvailable v -> ghost_var_available ctx v + | YamlEntryGlobal (g, task) -> + let g: V.t = Obj.obj g in + begin match g with + | `Right true when YamlWitness.entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type -> + let nodes = G.update (ctx.global g) in + let (variables, location_updates) = NodeSet.fold (fun node (variables, location_updates) -> + let (locked, unlocked, multithread) = G.node (ctx.global (V.node node)) in + let variables' = + Locked.fold (fun l acc -> + match mustlock_of_addr l with + | Some l when ghost_var_available ctx (Locked l) -> + let variable = WitnessGhost.variable' (Locked l) in + VariableSet.add variable acc + | _ -> + acc + ) (Locked.union locked unlocked) variables + in + let updates = + Locked.fold (fun l acc -> + match mustlock_of_addr l with + | Some l when ghost_var_available ctx (Locked l) -> + let update = WitnessGhost.update' (Locked l) GoblintCil.one in + update :: acc + | _ -> + acc + ) locked [] + in + let updates = + Unlocked.fold (fun l acc -> + match mustlock_of_addr l with + | Some l when ghost_var_available ctx (Locked l) -> + let update = WitnessGhost.update' (Locked l) GoblintCil.zero in + update :: acc + | _ -> + acc + ) unlocked updates + in + let (variables', updates) = + if not (GobConfig.get_bool "exp.earlyglobs") && multithread then ( + if ghost_var_available ctx Multithreaded then ( + let variable = WitnessGhost.variable' Multithreaded in + let update = WitnessGhost.update' Multithreaded GoblintCil.one in + let variables' = VariableSet.add variable variables' in + (variables', update :: updates) + ) + else + (variables', updates) + ) + else + (variables', updates) + in + match updates with + | [] -> (variables', location_updates) (* don't add location_update with no updates *) + | _ -> + let location_update = WitnessGhost.location_update' ~node ~updates in + (variables', location_update :: location_updates) + ) nodes (VariableSet.empty, []) + in + let entry = WitnessGhost.instrumentation_entry ~task ~variables:(VariableSet.elements variables) ~location_updates in + Queries.YS.singleton entry + | `Left _ -> Queries.Result.top q + | `Middle _ -> Queries.Result.top q + | `Right _ -> Queries.Result.top q + end + | InvariantGlobalNodes -> (G.threadcreate (ctx.global V.threadcreate): NodeSet.t) + | _ -> Queries.Result.top q +end + +let _ = + MCP.register_analysis ~dep:["mutexEvents"] (module Spec : MCPSpec) diff --git a/src/analyses/threadAnalysis.ml b/src/analyses/threadAnalysis.ml index 07f46e915d..435e1a6afe 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 -> (multiple, TS.bot (), false) in ctx.sideg tid eff; D.join ctx.local (D.singleton tid) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 8f8892b8be..1b430e651a 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -29,7 +29,7 @@ struct type inv = { exp: Cil.exp; - uuid: string; + token: WideningToken.t; } let invs: inv NH.t = NH.create 100 @@ -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; @@ -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; uuid} + 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; uuid} + 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 @@ -239,13 +239,13 @@ struct | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _ | InvariantSet _) -> M.info_noloc ~category:Witness "disabled entry of type %s" target_type | _ -> - M.info_noloc ~category:Witness "cannot unassume entry of type %s" target_type + M.warn_noloc ~category:Witness "cannot unassume entry of type %s" target_type in List.iter (fun yaml_entry -> match YamlWitnessType.Entry.of_yaml yaml_entry with | Ok entry -> unassume_entry entry - | Error (`Msg e) -> M.info_noloc ~category:Witness "couldn't parse entry: %s" e + | Error (`Msg e) -> M.error_noloc ~category:Witness "couldn't parse entry: %s" e ) yaml_entries let emit_unassume ctx = @@ -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 WideningTokenLifter.add tokens ) ); ctx.local diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 78013ec21d..b220afc0d9 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -562,7 +562,7 @@ struct let r = eq_set_clos e ctx.local in if M.tracing then M.tracel "var_eq" "equalset %a = %a" d_plainexp e Queries.ES.pretty r; r - | Queries.Invariant context when GobConfig.get_bool "witness.invariant.exact" -> (* only exact equalities here *) + | Queries.Invariant context when GobConfig.get_bool "ana.var_eq.invariant.enabled" && GobConfig.get_bool "witness.invariant.exact" -> (* only exact equalities here *) let scope = Node.find_fundec ctx.node in D.invariant ~scope ctx.local | _ -> Queries.Result.top x diff --git a/src/analyses/wrapperFunctionAnalysis.ml b/src/analyses/wrapperFunctionAnalysis.ml index eb9ec6ce02..32ca234e70 100644 --- a/src/analyses/wrapperFunctionAnalysis.ml +++ b/src/analyses/wrapperFunctionAnalysis.ml @@ -144,6 +144,8 @@ module MallocWrapper : MCPSpec = struct Format.dprintf "@tid:%s" (ThreadLifted.show t) in Format.asprintf "(alloc@sid:%s%t%t)" (Node.show_id node) tid uniq_count + + let typ _ = GoblintCil.voidType end module NodeVarinfoMap = RichVarinfo.BiVarinfoMap.Make(ThreadNode) diff --git a/src/autoTune.ml b/src/autoTune.ml index f59a10ee8a..05f651ee62 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -44,6 +44,34 @@ class functionVisitor(calling, calledBy, argLists, dynamicallyCalled) = object DoChildren end +exception Found +class findAllocsInLoops = object + inherit nopCilVisitor + + 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; ChangeDoChildrenPost(stmt, outOfLoop) + | _ -> DoChildren + + method! vinst = function + | 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 + | Malloc _ + | Alloca _ when inloop -> raise Found + | _ -> DoChildren + end + | _ -> DoChildren +end + type functionCallMaps = { calling: FunctionSet.t FunctionCallMap.t; calledBy: (FunctionSet.t * int) FunctionCallMap.t; @@ -230,18 +258,28 @@ let focusOnTermination (spec: Svcomp.Specification.t) = let focusOnTermination () = List.iter focusOnTermination (Svcomp.Specification.of_option ()) -let focusOnSpecification (spec: Svcomp.Specification.t) = +let concurrencySafety (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*) - set_bool "ana.int.def_exc" true | _ -> () -let focusOnSpecification () = - List.iter focusOnSpecification (Svcomp.Specification.of_option ()) +let noOverflows (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 findAllocsInLoops) (!Cilfacade.current_file); + set_int "ana.malloc.unique_address_count" 1 + with Found -> set_int "ana.malloc.unique_address_count" 0; + end + | _ -> () + +let focusOn (f : SvcompSpec.t -> unit) = + List.iter f (Svcomp.Specification.of_option ()) (*Detect enumerations and enable the "ana.int.enums" option*) exception EnumFound @@ -489,15 +527,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 specificationIsActivated () = - isActivated "specification" && get_string "ana.specification" <> "" - -let specificationTerminationIsActivated () = - isActivated "termination" - -let specificationMemSafetyIsActivated () = - isActivated "memsafetySpecification" - let chooseConfig file = let factors = collectFactors visitCilFileSameGlobals file in let fileCompplexity = estimateComplexity factors file in @@ -517,8 +546,9 @@ let chooseConfig file = if isActivated "mallocWrappers" then findMallocWrappers (); - if specificationIsActivated () then - focusOnSpecification (); + if isActivated "concurrencySafetySpecification" then focusOn concurrencySafety; + + if isActivated "noOverflows" then focusOn noOverflows; if isActivated "enums" && hasEnums file then set_bool "ana.int.enums" true; diff --git a/src/build-info/build_info_dune/dune b/src/build-info/build_info_dune/dune deleted file mode 100644 index ec46f4b3d1..0000000000 --- a/src/build-info/build_info_dune/dune +++ /dev/null @@ -1,6 +0,0 @@ -; goblint.build-info implementation which properly uses dune-build-info -(library - (name goblint_build_info_dune) - (public_name goblint.build-info.dune) - (implements goblint.build-info) - (libraries dune-build-info)) diff --git a/src/build-info/build_info_js/dune b/src/build-info/build_info_js/dune deleted file mode 100644 index 9400f564ff..0000000000 --- a/src/build-info/build_info_js/dune +++ /dev/null @@ -1,5 +0,0 @@ -; goblint.build-info implementation which works with js_of_ocaml and doesn't use dune-build-info -(library - (name goblint_build_info_js) - (public_name goblint.build-info.js) - (implements goblint.build-info)) diff --git a/src/build-info/build_info_js/dune_build_info.ml b/src/build-info/build_info_js/dune_build_info.ml deleted file mode 100644 index 002015cd31..0000000000 --- a/src/build-info/build_info_js/dune_build_info.ml +++ /dev/null @@ -1 +0,0 @@ -let statically_linked_libraries = [] diff --git a/src/build-info/dune b/src/build-info/dune index 1dd74c192c..085660531e 100644 --- a/src/build-info/dune +++ b/src/build-info/dune @@ -1,14 +1,9 @@ (include_subdirs no) -; virtual library to allow js build (for gobview) without dune-build-info -; dune-build-info seems to be incompatible with js_of_ocaml -; File "gobview/src/.App.eobjs/build_info_data.ml-gen", line 1: -; Error: Could not find the .cmi file for interface -; gobview/src/.App.eobjs/build_info_data.ml-gen. (library (name goblint_build_info) (public_name goblint.build-info) - (virtual_modules dune_build_info)) + (libraries dune-build-info)) (rule (target configVersion.ml) diff --git a/src/build-info/build_info_dune/dune_build_info.ml b/src/build-info/dune_build_info.ml similarity index 100% rename from src/build-info/build_info_dune/dune_build_info.ml rename to src/build-info/dune_build_info.ml diff --git a/src/cdomain/value/cdomains/threadIdDomain.ml b/src/cdomain/value/cdomains/threadIdDomain.ml index fff6734f27..226905ed6f 100644 --- a/src/cdomain/value/cdomains/threadIdDomain.ml +++ b/src/cdomain/value/cdomains/threadIdDomain.ml @@ -83,10 +83,10 @@ 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 _ = 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/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index f9f4b06ffb..b126d712bf 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -133,7 +133,7 @@ struct | _ -> false let is_mutex_type (t: typ): bool = match t with - | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" || info.tname = "pthread_cond_t" + | TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t" || info.tname = "pthread_spinlock_t" || info.tname = "pthread_cond_t" || info.tname = "pthread_rwlock_t" | TInt (IInt, attr) -> hasAttribute "mutex" attr | _ -> false 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 *) diff --git a/src/common/util/richVarinfo.ml b/src/common/util/richVarinfo.ml index d1918c40a6..6a27339eed 100644 --- a/src/common/util/richVarinfo.ml +++ b/src/common/util/richVarinfo.ml @@ -1,9 +1,9 @@ open GoblintCil -let create_var name = Cilfacade.create_var @@ makeGlobalVar name voidType +let create_var name typ = Cilfacade.create_var @@ makeGlobalVar name typ -let single ~name = - let vi = lazy (create_var name) in +let single ~name ~typ = + let vi = lazy (create_var name typ) in fun () -> Lazy.force vi @@ -21,6 +21,7 @@ module type G = sig include Hashtbl.HashedType val name_varinfo: t -> string + val typ: t -> typ end module type H = @@ -47,7 +48,7 @@ struct try XH.find !xh x with Not_found -> - let vi = create_var (X.name_varinfo x) in + let vi = create_var (X.name_varinfo x) (X.typ x) in store_f x vi; vi diff --git a/src/common/util/richVarinfo.mli b/src/common/util/richVarinfo.mli index 4e682734ee..d1c002bf84 100644 --- a/src/common/util/richVarinfo.mli +++ b/src/common/util/richVarinfo.mli @@ -2,7 +2,7 @@ open GoblintCil -val single: name:string -> (unit -> varinfo) +val single: name:string -> typ:typ -> (unit -> varinfo) module type VarinfoMap = sig @@ -18,6 +18,7 @@ module type G = sig include Hashtbl.HashedType val name_varinfo: t -> string + val typ: t -> typ end module type H = diff --git a/src/config/options.schema.json b/src/config/options.schema.json index 5d87eb51f6..43e2ad1d59 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,8 @@ "octagon", "wideningThresholds", "memsafetySpecification", + "concurrencySafetySpecification", + "noOverflows", "termination", "tmpSpecialAnalysis" ] @@ -552,7 +553,6 @@ "default": [ "congruence", "singleThreaded", - "specification", "mallocWrappers", "noRecursiveIntervals", "enums", @@ -561,6 +561,8 @@ "octagon", "wideningThresholds", "memsafetySpecification", + "concurrencySafetySpecification", + "noOverflows", "termination", "tmpSpecialAnalysis" ] @@ -795,6 +797,18 @@ "type": "boolean", "default": true }, + "local": { + "title": "ana.base.invariant.local", + "description": "Keep local variables in invariants", + "type": "boolean", + "default": true + }, + "global": { + "title": "ana.base.invariant.global", + "description": "Keep global variables in invariants", + "type": "boolean", + "default": true + }, "blobs": { "title": "ana.base.invariant.blobs", "description": "Whether to dump assertions about all blobs. Enabling this option may lead to unsound asserts.", @@ -1164,6 +1178,26 @@ } }, "additionalProperties": false + }, + "var_eq": { + "title": "ana.var_eq", + "type": "object", + "properties": { + "invariant": { + "title": "ana.var_eq.invariant", + "type": "object", + "properties": { + "enabled": { + "title": "ana.var_eq.invariant.enabled", + "description": "Generate var_eq analysis invariants", + "type": "boolean", + "default": true + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2592,6 +2626,13 @@ "description": "Emit invariants with typedef-ed types (e.g. in casts). Our validator cannot parse these.", "type": "boolean", "default": true + }, + "flow_insensitive-as": { + "title": "witness.invariant.flow_insensitive-as", + "description": "Emit flow-insensitive invariants as location invariants at certain locations.", + "type": "string", + "enum": ["flow_insensitive_invariant", "location_invariant", "invariant_set-location_invariant"], + "default": "flow_insensitive_invariant" } }, "additionalProperties": false @@ -2612,7 +2653,8 @@ "type": "string", "enum": [ "0.1", - "2.0" + "2.0", + "2.1" ], "default": "0.1" }, @@ -2630,7 +2672,8 @@ "loop_invariant_certificate", "precondition_loop_invariant_certificate", "invariant_set", - "violation_sequence" + "violation_sequence", + "ghost_instrumentation" ] }, "default": [ @@ -2672,7 +2715,7 @@ }, "strict": { "title": "witness.yaml.strict", - "description": "", + "description": "Fail YAML witness validation if there's an error/unsupported/disabled entry.", "type": "boolean", "default": false }, diff --git a/src/domain/partitionDomain.ml b/src/domain/partitionDomain.ml index 9675e9bfce..e97946e463 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) @@ -106,6 +106,9 @@ struct let show _ = "Partitions" + (* Top and bottom are reversed: + Bottom will be All (equations), i.e. contradiction, + Top will be empty set, i.e. no equations. *) let top = E.bot let bot = E.top let is_top = E.is_bot diff --git a/src/domains/events.ml b/src/domains/events.ml index b194847bac..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; uuids: string 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; 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 ", " WideningToken.pretty) tokens | Longjmped {lval} -> dprintf "Longjmped {lval=%a}" (docOpt (CilType.Lval.pretty ())) lval diff --git a/src/domains/queries.ml b/src/domains/queries.ml index b0ede0cfbf..fee44a6b24 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -10,6 +10,7 @@ module LS = VDQ.LS module TS = SetDomain.ToppedSet (CilType.Typ) (struct let topname = "All" end) module ES = SetDomain.Reverse (SetDomain.ToppedSet (CilType.Exp) (struct let topname = "All" end)) module VS = SetDomain.ToppedSet (CilType.Varinfo) (struct let topname = "All" end) +module NS = SetDomain.ToppedSet (Node) (struct let topname = "All" end) module NFL = WrapperFunctionAnalysis0.NodeFlatLattice module TC = WrapperFunctionAnalysis0.ThreadCreateUniqueCount @@ -62,6 +63,8 @@ type invariant_context = Invariant.context = { } [@@deriving ord, hash] +module YS = SetDomain.ToppedSet (YamlWitnessType.Entry) (struct let topname = "Top" end) + (** GADT for queries with specific result type. *) type _ t = @@ -114,6 +117,7 @@ type _ t = | MustJoinedThreads: ConcDomain.MustThreadSet.t t | ThreadsJoinedCleanly: MustBool.t t | MustProtectedVars: mustprotectedvars -> VS.t t + | MustProtectingLocks: CilType.Varinfo.t -> LockDomain.MustLockset.t t | Invariant: invariant_context -> Invariant.t t | InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *) | WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *) @@ -127,6 +131,9 @@ type _ t = | TmpSpecial: Mval.Exp.t -> ML.t t | MaySignedOverflow: exp -> MayBool.t t | GasExhausted: CilType.Fundec.t -> MustBool.t t + | YamlEntryGlobal: Obj.t * YamlWitnessType.Task.t -> YS.t t (** YAML witness entries for a global unknown ([Obj.t] represents [Spec.V.t]) and YAML witness task. *) + | GhostVarAvailable: WitnessGhostVar.t -> MayBool.t t + | InvariantGlobalNodes: NS.t t (** Nodes where YAML witness flow-insensitive invariants should be emitted as location invariants (if [witness.invariant.flow_insensitive-as] is configured to do so). *) (* [Spec.V.t] argument (as [Obj.t]) could be added, if this should be different for different flow-insensitive invariants. *) type 'a result = 'a @@ -185,6 +192,7 @@ struct | MustJoinedThreads -> (module ConcDomain.MustThreadSet) | ThreadsJoinedCleanly -> (module MustBool) | MustProtectedVars _ -> (module VS) + | MustProtectingLocks _ -> (module LockDomain.MustLockset) | Invariant _ -> (module Invariant) | InvariantGlobal _ -> (module Invariant) | WarnGlobal _ -> (module Unit) @@ -198,6 +206,9 @@ struct | TmpSpecial _ -> (module ML) | MaySignedOverflow _ -> (module MayBool) | GasExhausted _ -> (module MustBool) + | YamlEntryGlobal _ -> (module YS) + | GhostVarAvailable _ -> (module MayBool) + | InvariantGlobalNodes -> (module NS) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -255,6 +266,7 @@ struct | MustJoinedThreads -> ConcDomain.MustThreadSet.top () | ThreadsJoinedCleanly -> MustBool.top () | MustProtectedVars _ -> VS.top () + | MustProtectingLocks _ -> LockDomain.MustLockset.top () | Invariant _ -> Invariant.top () | InvariantGlobal _ -> Invariant.top () | WarnGlobal _ -> Unit.top () @@ -268,6 +280,9 @@ struct | TmpSpecial _ -> ML.top () | MaySignedOverflow _ -> MayBool.top () | GasExhausted _ -> MustBool.top () + | YamlEntryGlobal _ -> YS.top () + | GhostVarAvailable _ -> MayBool.top () + | InvariantGlobalNodes -> NS.top () end (* The type any_query can't be directly defined in Any as t, @@ -335,6 +350,10 @@ struct | Any (IsAllocVar _) -> 57 | Any (MaySignedOverflow _) -> 58 | Any (GasExhausted _) -> 59 + | Any (YamlEntryGlobal _) -> 60 + | Any (MustProtectingLocks _) -> 61 + | Any (GhostVarAvailable _) -> 62 + | Any InvariantGlobalNodes -> 63 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -382,14 +401,17 @@ struct | Any (WarnGlobal vi1), Any (WarnGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) | Any (Invariant i1), Any (Invariant i2) -> compare_invariant_context i1 i2 | Any (InvariantGlobal vi1), Any (InvariantGlobal vi2) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) + | Any (YamlEntryGlobal (vi1, task1)), Any (YamlEntryGlobal (vi2, task2)) -> Stdlib.compare (Hashtbl.hash vi1) (Hashtbl.hash vi2) (* TODO: compare task *) | Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *) | Any (MutexType m1), Any (MutexType m2) -> Mval.Unit.compare m1 m2 | Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2 + | Any (MustProtectingLocks g1), Any (MustProtectingLocks g2) -> CilType.Varinfo.compare g1 g2 | Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2 | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 | Any (MaySignedOverflow e1), Any (MaySignedOverflow e2) -> CilType.Exp.compare e1 e2 | Any (GasExhausted f1), Any (GasExhausted f2) -> CilType.Fundec.compare f1 f2 + | Any (GhostVarAvailable v1), Any (GhostVarAvailable v2) -> WitnessGhostVar.compare v1 v2 (* only argumentless queries should remain *) | _, _ -> Stdlib.compare (order a) (order b) @@ -427,12 +449,15 @@ struct | Any (Invariant i) -> hash_invariant_context i | Any (MutexType m) -> Mval.Unit.hash m | Any (InvariantGlobal vi) -> Hashtbl.hash vi + | Any (YamlEntryGlobal (vi, task)) -> Hashtbl.hash vi (* TODO: hash task *) | Any (MustProtectedVars m) -> hash_mustprotectedvars m + | Any (MustProtectingLocks g) -> CilType.Varinfo.hash g | Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv | Any (MaySignedOverflow e) -> CilType.Exp.hash e | Any (GasExhausted f) -> CilType.Fundec.hash f + | Any (GhostVarAvailable v) -> WitnessGhostVar.hash v (* IterSysVars: *) (* - argument is a function and functions cannot be compared in any meaningful way. *) (* - doesn't matter because IterSysVars is always queried from outside of the analysis, so MCP's query caching is not done for it. *) @@ -481,10 +506,12 @@ struct | Any MustJoinedThreads -> Pretty.dprintf "MustJoinedThreads" | Any ThreadsJoinedCleanly -> Pretty.dprintf "ThreadsJoinedCleanly" | Any (MustProtectedVars m) -> Pretty.dprintf "MustProtectedVars _" + | Any (MustProtectingLocks g) -> Pretty.dprintf "MustProtectingLocks _" | Any (Invariant i) -> Pretty.dprintf "Invariant _" | Any (WarnGlobal vi) -> Pretty.dprintf "WarnGlobal _" | Any (IterSysVars _) -> Pretty.dprintf "IterSysVars _" | Any (InvariantGlobal i) -> Pretty.dprintf "InvariantGlobal _" + | Any (YamlEntryGlobal (i, task)) -> Pretty.dprintf "YamlEntryGlobal _" | Any (MutexType (v,o)) -> Pretty.dprintf "MutexType _" | Any (EvalMutexAttr a) -> Pretty.dprintf "EvalMutexAttr _" | Any MayAccessed -> Pretty.dprintf "MayAccessed" @@ -497,6 +524,8 @@ struct | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv | Any (MaySignedOverflow e) -> Pretty.dprintf "MaySignedOverflow %a" CilType.Exp.pretty e | Any (GasExhausted f) -> Pretty.dprintf "GasExhausted %a" CilType.Fundec.pretty f + | Any (GhostVarAvailable v) -> Pretty.dprintf "GhostVarAvailable %a" WitnessGhostVar.pretty v + | Any InvariantGlobalNodes -> Pretty.dprintf "InvariantGlobalNodes" end let to_value_domain_ask (ask: ask) = diff --git a/src/dune b/src/dune index a307a237ae..10996e7ee8 100644 --- a/src/dune +++ b/src/dune @@ -88,7 +88,7 @@ (public_names goblint) (modes byte native) ; https://dune.readthedocs.io/en/stable/dune-files.html#linking-modes (modules goblint) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) + (libraries goblint.lib goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) @@ -96,7 +96,7 @@ (executable (name privPrecCompare) (modules privPrecCompare) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) + (libraries goblint.lib goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) @@ -104,7 +104,7 @@ (executable (name apronPrecCompare) (modules apronPrecCompare) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) + (libraries goblint.lib goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) @@ -112,7 +112,7 @@ (executable (name messagesCompare) (modules messagesCompare) - (libraries goblint.lib goblint.sites.dune goblint.build-info.dune goblint_std) + (libraries goblint.lib goblint_std) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall -open Goblint_std) ) 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.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/goblint_lib.ml b/src/goblint_lib.ml index 91f9837419..415fb21605 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -109,6 +109,7 @@ module MutexAnalysis = MutexAnalysis module MayLocks = MayLocks module SymbLocks = SymbLocks module Deadlock = Deadlock +module MutexGhosts = MutexGhosts (** {3 Threads} @@ -180,7 +181,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 @@ -350,6 +352,7 @@ module Graphml = Graphml module YamlWitness = YamlWitness module YamlWitnessType = YamlWitnessType +module WitnessGhost = WitnessGhost (** {3 Violation} 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/index.mld b/src/index.mld index f0d63a0fc7..a2ef15482e 100644 --- a/src/index.mld +++ b/src/index.mld @@ -44,15 +44,9 @@ The following libraries provide [goblint] package metadata for executables. {2 Library goblint.build-info} {!modules:Goblint_build_info} -This library is virtual and has the following implementations -- goblint.build-info.dune for native executables, -- goblint.build-info.js for js_of_ocaml executables. {2 Library goblint.sites} {!modules:Goblint_sites} -This library is virtual and has the following implementations -- goblint.sites.dune for native executables, -- goblint.sites.js for js_of_ocaml executables. {1 Independent utilities} diff --git a/src/lifters/longjmpLifter.ml b/src/lifters/longjmpLifter.ml index 1a28085abe..a093f8c703 100644 --- a/src/lifters/longjmpLifter.ml +++ b/src/lifters/longjmpLifter.ml @@ -66,6 +66,14 @@ struct | _ -> Queries.Result.top q end + | YamlEntryGlobal (g, task) -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (YamlEntryGlobal (Obj.repr g, task)) + | _ -> + Queries.Result.top q + end | IterSysVars (vq, vf) -> (* vars for S *) let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in diff --git a/src/lifters/recursionTermLifter.ml b/src/lifters/recursionTermLifter.ml index 2d9f45de60..37522305b9 100644 --- a/src/lifters/recursionTermLifter.ml +++ b/src/lifters/recursionTermLifter.ml @@ -106,6 +106,14 @@ struct | `Right v -> Queries.Result.top q end + | YamlEntryGlobal (v, task) -> + let v: V.t = Obj.obj v in + begin match v with + | `Left v -> + S.query (conv ctx) (YamlEntryGlobal (Obj.repr v, task)) + | `Right v -> + Queries.Result.top q + end | _ -> S.query (conv ctx) q let branch ctx = S.branch (conv ctx) diff --git a/src/lifters/specLifters.ml b/src/lifters/specLifters.ml index e8292bedc0..39c7799395 100644 --- a/src/lifters/specLifters.ml +++ b/src/lifters/specLifters.ml @@ -737,6 +737,14 @@ struct | `Right g -> Queries.Result.top q end + | YamlEntryGlobal (g, task) -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (YamlEntryGlobal (Obj.repr g, task)) + | `Right g -> + Queries.Result.top q + end | IterSysVars (vq, vf) -> (* vars for S *) let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in diff --git a/src/lifters/wideningToken.ml b/src/lifters/wideningToken.ml new file mode 100644 index 0000000000..0639521038 --- /dev/null +++ b/src/lifters/wideningToken.ml @@ -0,0 +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 (Uuid) (Index) diff --git a/src/lifters/wideningTokens.ml b/src/lifters/wideningTokenLifter.ml similarity index 98% rename from src/lifters/wideningTokens.ml rename to src/lifters/wideningTokenLifter.ml index 41bb5d8477..634468a9ca 100644 --- a/src/lifters/wideningTokens.ml +++ b/src/lifters/wideningTokenLifter.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. *) +module Token = WideningToken (** Widening token set. *) module TS = SetDomain.ToppedSet (Token) (struct let topname = "Top" end) 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 (); diff --git a/src/sites/dune b/src/sites/dune index d8663e37fe..6de3a5a32a 100644 --- a/src/sites/dune +++ b/src/sites/dune @@ -1,12 +1,10 @@ (include_subdirs no) -; virtual library to allow js build (for gobview) without dune-site -; dune-site seems to be incompatible with js_of_ocaml -; File "gobview/src/.App.eobjs/dune_site_data.ml-gen", line 1: -; Error: Could not find the .cmi file for interface -; gobview/src/.App.eobjs/dune_site_data.ml-gen. (library (name goblint_sites) (public_name goblint.sites) - (virtual_modules goblint_sites) - (libraries fpath)) + (libraries dune-site fpath)) + +(generate_sites_module + (module dunesite) + (sites goblint)) diff --git a/src/sites/sites_dune/goblint_sites.ml b/src/sites/goblint_sites.ml similarity index 100% rename from src/sites/sites_dune/goblint_sites.ml rename to src/sites/goblint_sites.ml diff --git a/src/sites/sites_dune/dune b/src/sites/sites_dune/dune deleted file mode 100644 index b7f90a8892..0000000000 --- a/src/sites/sites_dune/dune +++ /dev/null @@ -1,12 +0,0 @@ -; goblint.sites implementation which properly uses dune-site -(library - (name goblint_sites_dune) - (public_name goblint.sites.dune) - (implements goblint.sites) - (modules goblint_sites dunesite) - (private_modules dunesite) ; must also be in modules - (libraries dune-site)) - -(generate_sites_module - (module dunesite) - (sites goblint)) diff --git a/src/sites/sites_js/dune b/src/sites/sites_js/dune deleted file mode 100644 index 4e20871974..0000000000 --- a/src/sites/sites_js/dune +++ /dev/null @@ -1,6 +0,0 @@ -; goblint.sites implementation which works with js_of_ocaml and doesn't use dune-site -(library - (name goblint_sites_js) - (public_name goblint.sites.js) - (implements goblint.sites) - (modules goblint_sites)) diff --git a/src/sites/sites_js/goblint_sites.ml b/src/sites/sites_js/goblint_sites.ml deleted file mode 100644 index 3a7b353064..0000000000 --- a/src/sites/sites_js/goblint_sites.ml +++ /dev/null @@ -1,6 +0,0 @@ -let lib = [] -let lib_stub_include = [] -let lib_stub_src = [] -let lib_runtime_include = [] -let lib_runtime_src = [] -let conf = [] diff --git a/src/solver/td3.ml b/src/solver/td3.ml index c7bec621e3..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 *) @@ -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/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..6643b58eef 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", unknown [drop "j" []]); + ("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 *) @@ -504,7 +514,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" []]); @@ -1087,6 +1097,7 @@ let svcomp_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__VERIFIER_nondet_int", unknown []); (* declare invalidate actions to prevent invalidating globals when extern in regression tests *) ("__VERIFIER_nondet_size_t", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *) ("__VERIFIER_assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" }); (* only used if definition missing (e.g. in evalAssert transformed output) or extraspecial *) + ("reach_error", special [] @@ Abort); (* only used if definition missing (e.g. in evalAssert transformed output) or extraspecial *) ] [@@coverage off] 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/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 ( diff --git a/src/witness/witnessGhost.ml b/src/witness/witnessGhost.ml new file mode 100644 index 0000000000..3eaa8ef69b --- /dev/null +++ b/src/witness/witnessGhost.ml @@ -0,0 +1,32 @@ +(** Ghost variables for YAML witnesses. *) + +let enabled () = + YamlWitness.entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type + +module Var = WitnessGhostVar + +include Var + +module Map = RichVarinfo.BiVarinfoMap.Make (Var) + +include Map + +let variable' x = + let variable = name_varinfo x in + let type_ = String.trim (CilType.Typ.show (typ x)) in (* CIL printer puts space at the end of some types *) + let initial = CilType.Exp.show (initial x) in + YamlWitness.Entry.ghost_variable' ~variable ~type_ ~initial + +let update' x e = + let variable = name_varinfo x in + let expression = CilType.Exp.show e in + YamlWitness.Entry.ghost_update' ~variable ~expression + +let location_update' ~node ~updates = + let loc = Node.location node in + let location_function = (Node.find_fundec node).svar.vname in + let location = YamlWitness.Entry.location ~location:loc ~location_function in + YamlWitness.Entry.ghost_location_update' ~location ~updates + +let instrumentation_entry ~task ~variables ~location_updates = + YamlWitness.Entry.ghost_instrumentation ~task ~variables ~location_updates diff --git a/src/witness/witnessGhostVar.ml b/src/witness/witnessGhostVar.ml new file mode 100644 index 0000000000..0d71909ba0 --- /dev/null +++ b/src/witness/witnessGhostVar.ml @@ -0,0 +1,43 @@ +(** Ghost variables for YAML witnesses. *) + +type t = + | Locked of LockDomain.MustLock.t + | Multithreaded +[@@deriving eq, ord, hash] + +let name_varinfo = function + | Locked (v, os) -> + let name = + if CilType.Varinfo.equal v LibraryFunctions.verifier_atomic_var then + invalid_arg "__VERIFIER_atomic" + else + if RichVarinfo.BiVarinfoMap.Collection.mem_varinfo v then + Printf.sprintf "alloc_%s%d" (if v.vid < 0 then "m" else "") (abs v.vid) (* turn minus into valid C name *) + else + Basetype.Variables.show v + in + let rec offs: Offset.Z.t -> string = function + | `NoOffset -> "" + | `Field (f, os') -> "_" ^ f.fname ^ offs os' + | `Index (i, os') -> + assert Z.Compare.(i >= Z.zero); "_" ^ Z.to_string i + in + name ^ offs os ^ "_locked" + | Multithreaded -> "multithreaded" + +let show = name_varinfo + +include Printable.SimpleShow (struct + type nonrec t = t + let show = show + end) + +let describe_varinfo _ _ = "" + +let typ = function + | Locked _ + | Multithreaded -> GoblintCil.intType + +let initial = function + | Locked _ + | Multithreaded -> GoblintCil.zero diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 2bdd2ced4c..9d04b597fa 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -148,6 +148,35 @@ struct }; metadata = metadata (); } + + let ghost_variable' ~variable ~type_ ~(initial): GhostInstrumentation.Variable.t = { + name = variable; + scope = "global"; + type_; + initial = { + value = initial; + format = "c_expression"; + }; + } + + let ghost_update' ~variable ~(expression): GhostInstrumentation.Update.t = { + variable; + value = expression; + format = "c_expression"; + } + + let ghost_location_update' ~location ~(updates): GhostInstrumentation.LocationUpdate.t = { + location; + updates; + } + + let ghost_instrumentation ~task ~variables ~(location_updates): Entry.t = { + entry_type = GhostInstrumentation { + ghost_variables = variables; + ghost_updates = location_updates; + }; + metadata = metadata ~task (); + } end let yaml_entries_to_file yaml_entries file = @@ -249,6 +278,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 +302,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 +332,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)? *) @@ -310,24 +346,53 @@ struct entries in + let invariant_global_nodes = lazy (R.ask_global InvariantGlobalNodes) in + + let fold_flow_insensitive_as_location ~inv f acc = + (* Currently same invariants (from InvariantGlobal query) for all nodes (from InvariantGlobalNodes query). + The alternative would be querying InvariantGlobal per local unknown when looping over them to generate location invariants. + See: https://github.com/goblint/analyzer/pull/1394#discussion_r1698149514. *) + let invs = WitnessUtil.InvariantExp.process_exp inv in + Queries.NS.fold (fun n acc -> + let fundec = Node.find_fundec n in + match WitnessInvariant.location_location n with (* Not just using Node.location because it returns expression location which may be invalid for location invariant (e.g. inside conditional). *) + | Some loc -> + let location_function = fundec.svar.vname in + let location = Entry.location ~location:loc ~location_function in + List.fold_left (fun acc inv -> + f ~location ~inv acc + ) acc invs + | None -> acc + ) (Lazy.force invariant_global_nodes) acc + in + (* Generate flow-insensitive invariants *) let entries = if entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type then ( GHT.fold (fun g v acc -> match g with - | `Left g -> (* Spec global *) - begin match R.ask_global (InvariantGlobal (Obj.repr g)) with - | `Lifted inv -> + | `Left g -> (* global unknown from analysis Spec *) + begin match R.ask_global (InvariantGlobal (Obj.repr g)), GobConfig.get_string "witness.invariant.flow_insensitive-as" with + | `Lifted inv, "flow_insensitive_invariant" -> let invs = WitnessUtil.InvariantExp.process_exp inv in 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 *) + | `Lifted inv, "location_invariant" -> + fold_flow_insensitive_as_location ~inv (fun ~location ~inv acc -> + let invariant = Entry.invariant (CilType.Exp.show inv) in + let entry = Entry.location_invariant ~task ~location ~invariant in + incr cnt_location_invariant; + entry :: acc + ) acc + | `Lifted _, _ + | `Bot, _ | `Top, _ -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) acc end - | `Right _ -> (* contexts global *) + | `Right _ -> (* global unknown for FromSpec contexts *) acc ) gh entries ) @@ -335,6 +400,33 @@ struct entries in + (* Generate flow-insensitive entries (ghost instrumentation) *) + let entries = + if entry_type_enabled YamlWitnessType.GhostInstrumentation.entry_type then ( + (* TODO: only at most one ghost_instrumentation entry can ever be produced, so this fold and deduplication is overkill *) + let module EntrySet = Queries.YS in + fst @@ GHT.fold (fun g v accs -> + match g with + | `Left g -> (* global unknown from analysis Spec *) + begin match R.ask_global (YamlEntryGlobal (Obj.repr g, task)) with + | `Lifted _ as inv -> + Queries.YS.fold (fun entry (acc, acc') -> + if EntrySet.mem entry acc' then (* deduplicate only with other global entries because local ones have different locations anyway *) + accs + else + (entry :: acc, EntrySet.add entry acc') + ) inv accs + | `Top -> + accs + end + | `Right _ -> (* global unknown for FromSpec contexts *) + accs + ) gh (entries, EntrySet.empty ()) + ) + else + entries + in + (* Generate precondition loop invariants. We do this in three steps: 1. Collect contexts for each function @@ -437,12 +529,12 @@ struct (* Generate invariant set *) let entries = - if entry_type_enabled YamlWitnessType.InvariantSet.entry_type then ( + if entry_type_enabled YamlWitnessType.InvariantSet.entry_type || entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type && GobConfig.get_string "witness.invariant.flow_insensitive-as" = "invariant_set-location_invariant" then ( let invariants = [] in (* Generate location invariants *) let invariants = - if invariant_type_enabled YamlWitnessType.InvariantSet.LocationInvariant.invariant_type then ( + if entry_type_enabled YamlWitnessType.InvariantSet.entry_type && invariant_type_enabled YamlWitnessType.InvariantSet.LocationInvariant.invariant_type then ( LH.fold (fun loc ns acc -> let inv = List.fold_left (fun acc n -> let local = try NH.find (Lazy.force nh) n with Not_found -> Spec.D.bot () in @@ -459,6 +551,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)? *) @@ -471,7 +564,7 @@ struct (* Generate loop invariants *) let invariants = - if invariant_type_enabled YamlWitnessType.InvariantSet.LoopInvariant.invariant_type then ( + if entry_type_enabled YamlWitnessType.InvariantSet.entry_type && invariant_type_enabled YamlWitnessType.InvariantSet.LoopInvariant.invariant_type then ( LH.fold (fun loc ns acc -> if WitnessInvariant.emit_loop_head then ( (* TODO: remove double condition? *) let inv = List.fold_left (fun acc n -> @@ -488,6 +581,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)? *) @@ -501,6 +595,31 @@ struct invariants in + (* Generate flow-insensitive invariants as location invariants *) + let invariants = + if entry_type_enabled YamlWitnessType.FlowInsensitiveInvariant.entry_type && GobConfig.get_string "witness.invariant.flow_insensitive-as" = "invariant_set-location_invariant" then ( + GHT.fold (fun g v acc -> + match g with + | `Left g -> (* global unknown from analysis Spec *) + begin match R.ask_global (InvariantGlobal (Obj.repr g)) with + | `Lifted inv -> + fold_flow_insensitive_as_location ~inv (fun ~location ~inv acc -> + let invariant = CilType.Exp.show inv in + let invariant = Entry.location_invariant' ~location ~invariant in + incr cnt_location_invariant; + invariant :: acc + ) acc + | `Bot | `Top -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) + acc + end + | `Right _ -> (* global unknown for FromSpec contexts *) + acc + ) gh invariants + ) + else + invariants + in + let invariants = List.rev invariants in let entry = Entry.invariant_set ~task ~invariants in entry :: entries @@ -512,6 +631,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); ]; @@ -595,7 +717,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; @@ -839,7 +961,7 @@ struct None | _ -> incr cnt_unsupported; - M.info_noloc ~category:Witness "cannot validate entry of type %s" target_type; + M.warn_noloc ~category:Witness "cannot validate entry of type %s" target_type; None in @@ -851,7 +973,7 @@ struct Option.to_list yaml_certificate_entry @ yaml_entry :: yaml_entries' | Error (`Msg e) -> incr cnt_error; - M.info_noloc ~category:Witness "couldn't parse entry: %s" e; + M.error_noloc ~category:Witness "couldn't parse entry: %s" e; yaml_entry :: yaml_entries' ) [] yaml_entries in @@ -879,7 +1001,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 | _ -> diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 4fc2029801..7a57197a6f 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -9,6 +9,7 @@ struct command_line: string option; (* TODO: description *) } + [@@deriving eq, ord, hash] let to_yaml {name; version; command_line} = `O ([ @@ -39,6 +40,7 @@ struct language: string; specification: string option; } + [@@deriving eq, ord, hash] let to_yaml {input_files; input_file_hashes; data_model; language; specification} = `O ([ @@ -78,6 +80,7 @@ struct producer: Producer.t; task: Task.t option; } + [@@deriving eq, ord, hash] let to_yaml {format_version; uuid; creation_time; producer; task} = `O ([ @@ -111,7 +114,7 @@ struct column: int option; function_: string option; } - [@@deriving ord] + [@@deriving eq, ord, hash] let to_yaml {file_name; file_hash; line; column; function_} = `O ([ @@ -155,7 +158,7 @@ struct type_: string; format: string; } - [@@deriving ord] + [@@deriving eq, ord, hash] let to_yaml {string; type_; format} = `O [ @@ -178,7 +181,7 @@ struct location: Location.t; loop_invariant: Invariant.t; } - [@@deriving ord] + [@@deriving eq, ord, hash] let entry_type = "loop_invariant" @@ -201,7 +204,7 @@ struct location: Location.t; location_invariant: Invariant.t; } - [@@deriving ord] + [@@deriving eq, ord, hash] let entry_type = "location_invariant" @@ -223,7 +226,7 @@ struct type t = { flow_insensitive_invariant: Invariant.t; } - [@@deriving ord] + [@@deriving eq, ord, hash] let entry_type = "flow_insensitive_invariant" @@ -245,7 +248,7 @@ struct loop_invariant: Invariant.t; precondition: Invariant.t; } - [@@deriving ord] + [@@deriving eq, ord, hash] let entry_type = "precondition_loop_invariant" @@ -273,7 +276,7 @@ struct value: string; format: string; } - [@@deriving ord] + [@@deriving eq, ord, hash] let invariant_type = "loop_invariant" @@ -305,7 +308,7 @@ struct type t = | LocationInvariant of LocationInvariant.t | LoopInvariant of LoopInvariant.t - [@@deriving ord] + [@@deriving eq, ord, hash] let invariant_type = function | LocationInvariant _ -> LocationInvariant.invariant_type @@ -333,7 +336,7 @@ struct type t = { invariant_type: InvariantType.t; } - [@@deriving ord] + [@@deriving eq, ord, hash] let to_yaml {invariant_type} = `O [ @@ -352,7 +355,7 @@ struct type t = { content: Invariant.t list; } - [@@deriving ord] + [@@deriving eq, ord, hash] let entry_type = "invariant_set" @@ -372,7 +375,7 @@ struct type_: string; file_hash: string; } - [@@deriving ord] + [@@deriving eq, ord, hash] let to_yaml {uuid; type_; file_hash} = `O [ @@ -396,7 +399,7 @@ struct type_: string; format: string; } - [@@deriving ord] + [@@deriving eq, ord, hash] let to_yaml {string; type_; format} = `O [ @@ -419,7 +422,7 @@ struct target: Target.t; certification: Certification.t; } - [@@deriving ord] + [@@deriving eq, ord, hash] let entry_type = "loop_invariant_certificate" @@ -447,15 +450,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 eq, ord, hash] + + 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] + [@@deriving eq, ord, hash] let to_yaml {value; format} = `O ([ - ("value", `String value); + ("value", Value.to_yaml value); ] @ (match format with | Some format -> [ ("format", `String format); @@ -466,7 +489,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 @@ -478,7 +501,7 @@ struct action: string; constraint_: Constraint.t; } - [@@deriving ord] + [@@deriving eq, ord, hash] let waypoint_type = "assumption" @@ -503,7 +526,7 @@ struct location: Location.t; action: string; } - [@@deriving ord] + [@@deriving eq, ord, hash] let waypoint_type = "target" @@ -550,7 +573,7 @@ struct | FunctionEnter of FunctionEnter.t | FunctionReturn of FunctionReturn.t | Branching of Branching.t - [@@deriving ord] + [@@deriving eq, ord, hash] let waypoint_type = function | Assumption _ -> Assumption.waypoint_type @@ -593,7 +616,7 @@ struct type t = { waypoint_type: WaypointType.t; } - [@@deriving ord] + [@@deriving eq, ord, hash] let to_yaml {waypoint_type} = `O [ @@ -614,7 +637,7 @@ struct type t = { segment: Waypoint.t list; } - [@@deriving ord] + [@@deriving eq, ord, hash] let to_yaml {segment} = `O [("segment", `A (List.map Waypoint.to_yaml segment))] @@ -628,7 +651,7 @@ struct type t = { content: Segment.t list; } - [@@deriving ord] + [@@deriving eq, ord, hash] let entry_type = "violation_sequence" @@ -641,6 +664,126 @@ struct {content} end +module GhostInstrumentation = +struct + + module Initial = + struct + type t = { + value: string; + format: string; + } + [@@deriving eq, ord, hash] + + let to_yaml {value; format} = + `O [ + ("value", `String value); + ("format", `String format); + ] + + let of_yaml y = + let open GobYaml in + let+ value = y |> find "value" >>= to_string + and+ format = y |> find "format" >>= to_string in + {value; format} + end + + module Variable = + struct + type t = { + name: string; + scope: string; + type_: string; + initial: Initial.t; + } + [@@deriving eq, ord, hash] + + let to_yaml {name; scope; type_; initial} = + `O [ + ("name", `String name); + ("scope", `String scope); + ("type", `String type_); + ("initial", Initial.to_yaml initial); + ] + + let of_yaml y = + let open GobYaml in + let+ name = y |> find "name" >>= to_string + and+ scope = y |> find "scope" >>= to_string + and+ type_ = y |> find "type" >>= to_string + and+ initial = y |> find "initial" >>= Initial.of_yaml in + {name; scope; type_; initial} + end + + module Update = + struct + type t = { + variable: string; + value: string; + format: string; + } + [@@deriving eq, ord, hash] + + let to_yaml {variable; value; format} = + `O [ + ("variable", `String variable); + ("value", `String value); + ("format", `String format); + ] + + let of_yaml y = + let open GobYaml in + let+ variable = y |> find "variable" >>= to_string + and+ value = y |> find "value" >>= to_string + and+ format = y |> find "format" >>= to_string in + {variable; value; format} + end + + module LocationUpdate = + struct + type t = { + location: Location.t; + updates: Update.t list; + } + [@@deriving eq, ord, hash] + + let to_yaml {location; updates} = + `O [ + ("location", Location.to_yaml location); + ("updates", `A (List.map Update.to_yaml updates)); + ] + + let of_yaml y = + let open GobYaml in + let+ location = y |> find "location" >>= Location.of_yaml + and+ updates = y |> find "updates" >>= list >>= list_map Update.of_yaml in + {location; updates} + end + + type t = { + ghost_variables: Variable.t list; + ghost_updates: LocationUpdate.t list; + } + [@@deriving eq, ord, hash] + + let entry_type = "ghost_instrumentation" + + let to_yaml' {ghost_variables; ghost_updates} = + [("content", + `O [ + ("ghost_variables", `A (List.map Variable.to_yaml ghost_variables)); + ("ghost_updates", `A (List.map LocationUpdate.to_yaml ghost_updates)); + ]) + ] + + let of_yaml y = + let open GobYaml in + let* content = y |> find "content" in + let+ ghost_variables = content |> find "ghost_variables" >>= list >>= list_map Variable.of_yaml + and+ ghost_updates = content |> find "ghost_updates" >>= list >>= list_map LocationUpdate.of_yaml in + {ghost_variables; ghost_updates} +end + (* TODO: could maybe use GADT, but adds ugly existential layer to entry type pattern matching *) module EntryType = struct @@ -653,7 +796,8 @@ struct | PreconditionLoopInvariantCertificate of PreconditionLoopInvariantCertificate.t | InvariantSet of InvariantSet.t | ViolationSequence of ViolationSequence.t - [@@deriving ord] + | GhostInstrumentation of GhostInstrumentation.t + [@@deriving eq, ord, hash] let entry_type = function | LocationInvariant _ -> LocationInvariant.entry_type @@ -664,6 +808,7 @@ struct | PreconditionLoopInvariantCertificate _ -> PreconditionLoopInvariantCertificate.entry_type | InvariantSet _ -> InvariantSet.entry_type | ViolationSequence _ -> ViolationSequence.entry_type + | GhostInstrumentation _ -> GhostInstrumentation.entry_type let to_yaml' = function | LocationInvariant x -> LocationInvariant.to_yaml' x @@ -674,6 +819,7 @@ struct | PreconditionLoopInvariantCertificate x -> PreconditionLoopInvariantCertificate.to_yaml' x | InvariantSet x -> InvariantSet.to_yaml' x | ViolationSequence x -> ViolationSequence.to_yaml' x + | GhostInstrumentation x -> GhostInstrumentation.to_yaml' x let of_yaml y = let open GobYaml in @@ -702,16 +848,24 @@ struct else if entry_type = ViolationSequence.entry_type then let+ x = y |> ViolationSequence.of_yaml in ViolationSequence x + else if entry_type = GhostInstrumentation.entry_type then + let+ x = y |> GhostInstrumentation.of_yaml in + GhostInstrumentation x else - Error (`Msg "entry_type") + Error (`Msg ("entry_type " ^ entry_type)) end module Entry = struct + include Printable.StdLeaf + type t = { entry_type: EntryType.t; - metadata: Metadata.t; + metadata: Metadata.t [@equal fun _ _ -> true] [@compare fun _ _ -> 0] [@hash fun _ -> 1]; } + [@@deriving eq, ord, hash] + + let name () = "YAML entry" let to_yaml {entry_type; metadata} = `O ([ @@ -724,4 +878,10 @@ struct let+ metadata = y |> find "metadata" >>= Metadata.of_yaml and+ entry_type = y |> EntryType.of_yaml in {entry_type; metadata} + + let pp ppf x = Yaml.pp ppf (to_yaml x) + include Printable.SimpleFormat (struct + type nonrec t = t + let pp = pp + end) end diff --git a/tests/regression/03-practical/21-pfscan_combine_minimal.c b/tests/regression/03-practical/21-pfscan_combine_minimal.c index abdef0627b..b8fc7948b2 100644 --- a/tests/regression/03-practical/21-pfscan_combine_minimal.c +++ b/tests/regression/03-practical/21-pfscan_combine_minimal.c @@ -18,7 +18,7 @@ int pqueue_init(PQUEUE *qp) void pqueue_close(PQUEUE *qp ) { - pthread_mutex_lock(& qp->mtx); + pthread_mutex_lock(& qp->mtx); // TODO (OSX): WARN (must double locking) qp->closed = 1; pthread_mutex_unlock(& qp->mtx); return; @@ -26,7 +26,7 @@ void pqueue_close(PQUEUE *qp ) int pqueue_put(PQUEUE *qp) { - pthread_mutex_lock(& qp->mtx); + pthread_mutex_lock(& qp->mtx); // TODO (OSX): WARN (must double locking) if (qp->closed) { // pfscan actually has a bug and is missing the following unlock at early return // pthread_mutex_unlock(& qp->mtx); 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/04-mutex/32-allfuns.c b/tests/regression/04-mutex/32-allfuns.c index b59c487c53..e6b88e47ce 100644 --- a/tests/regression/04-mutex/32-allfuns.c +++ b/tests/regression/04-mutex/32-allfuns.c @@ -8,11 +8,11 @@ pthread_mutex_t B_mutex = PTHREAD_MUTEX_INITIALIZER; void t1() { pthread_mutex_lock(&A_mutex); myglobal++; //RACE! - pthread_mutex_lock(&A_mutex); + pthread_mutex_unlock(&A_mutex); } void t2() { pthread_mutex_lock(&B_mutex); myglobal++; //RACE! - pthread_mutex_lock(&B_mutex); + pthread_mutex_unlock(&B_mutex); } diff --git a/tests/regression/06-symbeq/20-mult_accs_nr.c b/tests/regression/06-symbeq/20-mult_accs_nr.c index 7d66e3f5d2..50c8f19a62 100644 --- a/tests/regression/06-symbeq/20-mult_accs_nr.c +++ b/tests/regression/06-symbeq/20-mult_accs_nr.c @@ -15,7 +15,7 @@ void *t_fun(void *arg) { pthread_mutex_lock(&s->mutex); s->data = 5; // NORACE s->lore = 6; // NORACE - pthread_mutex_lock(&s->mutex); + pthread_mutex_unlock(&s->mutex); return NULL; } diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.c b/tests/regression/06-symbeq/21-mult_accs_rc.c index 62550fab55..4acadb4a58 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.c +++ b/tests/regression/06-symbeq/21-mult_accs_rc.c @@ -14,7 +14,7 @@ void *t_fun(void *arg) { pthread_mutex_lock(&s->mutex); s = get_s(); s->data = 5; // RACE! - pthread_mutex_lock(&s->mutex); + pthread_mutex_unlock(&s->mutex); return NULL; } diff --git a/tests/regression/06-symbeq/21-mult_accs_rc.t b/tests/regression/06-symbeq/21-mult_accs_rc.t index ca2e219b05..2eacd0382e 100644 --- a/tests/regression/06-symbeq/21-mult_accs_rc.t +++ b/tests/regression/06-symbeq/21-mult_accs_rc.t @@ -3,7 +3,7 @@ Disable info messages because race summary contains (safe) memory location count $ goblint --enable warn.deterministic --disable warn.info --enable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" 21-mult_accs_rc.c 2>&1 | tee default-output-1.txt [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:14:3-14:32) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:16:3-16:14) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:17:3-17:32) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:17:3-17:34) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:28:3-28:16) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:29:3-29:15) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:34:3-34:9) @@ -11,7 +11,8 @@ Disable info messages because race summary contains (safe) memory location count write with thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) write with [symblock:{p-lock:*.mutex}, mhp:{created={[main, t_fun@21-mult_accs_rc.c:31:3-31:37]}}, thread:[main]] (conf. 100) (exp: & *d) (21-mult_accs_rc.c:34:3-34:9) [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:14:3-14:32) - [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:17:3-17:32) + [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:17:3-17:34) + [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:17:3-17:34) [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:33:3-33:24) [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) @@ -23,14 +24,15 @@ Disable info messages because race summary contains (safe) memory location count $ goblint --enable warn.deterministic --disable warn.info --disable ana.race.direct-arithmetic --set ana.activated[+] "'var_eq'" --set ana.activated[+] "'symb_locks'" --enable allglobs 21-mult_accs_rc.c 2>&1 | tee default-output-2.txt [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:14:3-14:32) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:16:3-16:14) - [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:17:3-17:32) + [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:17:3-17:34) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:28:3-28:16) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:29:3-29:15) [Warning][Behavior > Undefined > NullPointerDereference][CWE-476] May dereference NULL pointer (21-mult_accs_rc.c:34:3-34:9) [Success][Race] Memory location (struct s).data (safe): write with thread:[main, t_fun@21-mult_accs_rc.c:31:3-31:37] (conf. 100) (exp: & s->data) (21-mult_accs_rc.c:16:3-16:14) [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:14:3-14:32) - [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:17:3-17:32) + [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:17:3-17:34) + [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:17:3-17:34) [Warning][Unknown] locking NULL mutex (21-mult_accs_rc.c:33:3-33:24) [Warning][Unknown] unlocking NULL mutex (21-mult_accs_rc.c:35:3-35:26) [Warning][Unknown] unlocking unknown mutex which may not be held (21-mult_accs_rc.c:35:3-35:26) diff --git a/tests/regression/06-symbeq/34-var_eq-exponential-context.c b/tests/regression/06-symbeq/34-var_eq-exponential-context.c index bc478374f0..8b9b76a89e 100644 --- a/tests/regression/06-symbeq/34-var_eq-exponential-context.c +++ b/tests/regression/06-symbeq/34-var_eq-exponential-context.c @@ -1,5 +1,5 @@ // SKIP PARAM: --set ana.activated[+] var_eq - +// NOTIMEOUT? void level0(int *p) { } diff --git a/tests/regression/06-symbeq/41-var_eq_multithread.c b/tests/regression/06-symbeq/47-var_eq_multithread.c similarity index 100% rename from tests/regression/06-symbeq/41-var_eq_multithread.c rename to tests/regression/06-symbeq/47-var_eq_multithread.c diff --git a/tests/regression/09-regions/31-equ_rc.c b/tests/regression/09-regions/31-equ_rc.c index 7cea370c58..2f3aff7f63 100644 --- a/tests/regression/09-regions/31-equ_rc.c +++ b/tests/regression/09-regions/31-equ_rc.c @@ -15,7 +15,7 @@ struct s { void *t_fun(void *arg) { pthread_mutex_lock(&A.mutex); B.datum = 5; // RACE! - pthread_mutex_lock(&A.mutex); + pthread_mutex_unlock(&A.mutex); return NULL; } diff --git a/tests/regression/09-regions/32-equ_nr.c b/tests/regression/09-regions/32-equ_nr.c index d9b909546e..a242448ba8 100644 --- a/tests/regression/09-regions/32-equ_nr.c +++ b/tests/regression/09-regions/32-equ_nr.c @@ -15,7 +15,7 @@ struct s { void *t_fun(void *arg) { pthread_mutex_lock(&A.mutex); A.datum = 5; // NORACE - pthread_mutex_lock(&A.mutex); + pthread_mutex_unlock(&A.mutex); return NULL; } diff --git a/tests/regression/10-synch/29-multiple-created-only.c b/tests/regression/10-synch/29-multiple-created-only.c new file mode 100644 index 0000000000..e65021caaf --- /dev/null +++ b/tests/regression/10-synch/29-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/10-synch/30-no-crash.c b/tests/regression/10-synch/30-no-crash.c new file mode 100644 index 0000000000..c9a6c7d88b --- /dev/null +++ b/tests/regression/10-synch/30-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; +} 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/13-privatized/04-priv_multi.t b/tests/regression/13-privatized/04-priv_multi.t new file mode 100644 index 0000000000..1f6dff3fdc --- /dev/null +++ b/tests/regression/13-privatized/04-priv_multi.t @@ -0,0 +1,335 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 04-priv_multi.c + [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) + [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) + [Warning][Deadcode] Function 'dispose' has dead code: + on line 53 (04-priv_multi.c:53-53) + on line 56 (04-priv_multi.c:56-56) + [Warning][Deadcode] Function 'process' has dead code: + on line 37 (04-priv_multi.c:37-37) + on line 40 (04-priv_multi.c:40-40) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 40 + dead: 4 + total lines: 44 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:25:10-25:11) + [Warning][Deadcode][CWE-571] condition 'A > 0' is always true (04-priv_multi.c:27:9-27:14) + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:45:10-45:11) + [Warning][Deadcode][CWE-571] condition 'B > 0' is always true (04-priv_multi.c:47:9-47:14) + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 3 + total generation entries: 4 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + + $ yamlWitnessStrip < witness.yml | tee witness.flow_insensitive.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: mutex_A_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: mutex_B_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 15 + column: 5 + function: generate + updates: + - variable: mutex_A_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 18 + column: 5 + function: generate + updates: + - variable: mutex_A_locked + value: "0" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 26 + column: 5 + function: process + updates: + - variable: mutex_A_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 29 + column: 7 + function: process + updates: + - variable: mutex_B_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 32 + column: 7 + function: process + updates: + - variable: mutex_B_locked + value: "0" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 34 + column: 7 + function: process + updates: + - variable: mutex_A_locked + value: "0" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 46 + column: 5 + function: dispose + updates: + - variable: mutex_B_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 49 + column: 7 + function: dispose + updates: + - variable: mutex_B_locked + value: "0" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 63 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 68 + column: 5 + function: main + updates: + - variable: mutex_A_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 69 + column: 5 + function: main + updates: + - variable: mutex_B_locked + value: "1" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 73 + column: 5 + function: main + updates: + - variable: mutex_B_locked + value: "0" + format: c_expression + - location: + file_name: 04-priv_multi.c + file_hash: $FILE_HASH + line: 74 + column: 5 + function: main + updates: + - variable: mutex_A_locked + value: "0" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (mutex_B_locked || (mutex_A_locked || B == 5))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (mutex_A_locked || A == 5)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || ((0 <= B && B <= 127) && B != 0)' + type: assertion + format: C + +Flow-insensitive invariants as location invariants. + + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' --set witness.invariant.flow_insensitive-as location_invariant 04-priv_multi.c + [Success][Assert] Assertion "p == 5" will succeed (04-priv_multi.c:50:7-50:30) + [Success][Assert] Assertion "A == B" will succeed (04-priv_multi.c:71:5-71:28) + [Warning][Deadcode] Function 'dispose' has dead code: + on line 53 (04-priv_multi.c:53-53) + on line 56 (04-priv_multi.c:56-56) + [Warning][Deadcode] Function 'process' has dead code: + on line 37 (04-priv_multi.c:37-37) + on line 40 (04-priv_multi.c:40-40) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 40 + dead: 4 + total lines: 44 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:25:10-25:11) + [Warning][Deadcode][CWE-571] condition 'A > 0' is always true (04-priv_multi.c:27:9-27:14) + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (04-priv_multi.c:45:10-45:11) + [Warning][Deadcode][CWE-571] condition 'B > 0' is always true (04-priv_multi.c:47:9-47:14) + [Info][Witness] witness generation summary: + location invariants: 9 + loop invariants: 0 + flow-insensitive invariants: 0 + total generation entries: 10 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + + $ yamlWitnessStrip < witness.yml > witness.location.yml + +Location invariant at `for` loop in `main` should be on column 3, not 7. + + $ diff witness.flow_insensitive.yml witness.location.yml + 153,154c153,160 + < - entry_type: flow_insensitive_invariant + < flow_insensitive_invariant: + --- + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 67 + > column: 3 + > function: main + > location_invariant: + 158,159c164,171 + < - entry_type: flow_insensitive_invariant + < flow_insensitive_invariant: + --- + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 67 + > column: 3 + > function: main + > location_invariant: + 163,164c175,248 + < - entry_type: flow_insensitive_invariant + < flow_insensitive_invariant: + --- + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 67 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || ((0 <= B && B <= 127) && B != 0)' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 65 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || (mutex_B_locked || (mutex_A_locked || B == 5))' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 65 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || (mutex_A_locked || A == 5)' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 65 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || ((0 <= B && B <= 127) && B != 0)' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 64 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || (mutex_B_locked || (mutex_A_locked || B == 5))' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 64 + > column: 3 + > function: main + > location_invariant: + > string: '! multithreaded || (mutex_A_locked || A == 5)' + > type: assertion + > format: C + > - entry_type: location_invariant + > location: + > file_name: 04-priv_multi.c + > file_hash: $FILE_HASH + > line: 64 + > column: 3 + > function: main + > location_invariant: + [1] diff --git a/tests/regression/13-privatized/25-struct_nr.t b/tests/regression/13-privatized/25-struct_nr.t new file mode 100644 index 0000000000..fa00b2a5a5 --- /dev/null +++ b/tests/regression/13-privatized/25-struct_nr.t @@ -0,0 +1,97 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 25-struct_nr.c + [Success][Assert] Assertion "glob1 == 5" will succeed (25-struct_nr.c:26:3-26:30) + [Success][Assert] Assertion "t == 5" will succeed (25-struct_nr.c:16:3-16:26) + [Success][Assert] Assertion "glob1 == -10" will succeed (25-struct_nr.c:18:3-18:32) + [Success][Assert] Assertion "glob1 == 6" will succeed (25-struct_nr.c:30:3-30:30) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 2 + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: lock1_mutex_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: + - variable: lock1_mutex_locked + value: "1" + format: c_expression + - location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: t_fun + updates: + - variable: lock1_mutex_locked + value: "0" + format: c_expression + - location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 27 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + updates: + - variable: lock1_mutex_locked + value: "1" + format: c_expression + - location: + file_name: 25-struct_nr.c + file_hash: $FILE_HASH + line: 32 + column: 3 + function: main + updates: + - variable: lock1_mutex_locked + value: "0" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (lock1_mutex_locked || glob1 == 5)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || ((-128 <= glob1 && glob1 <= 127) && glob1 != 0)' + type: assertion + format: C diff --git a/tests/regression/13-privatized/74-mutex.t b/tests/regression/13-privatized/74-mutex.t new file mode 100644 index 0000000000..4b370db387 --- /dev/null +++ b/tests/regression/13-privatized/74-mutex.t @@ -0,0 +1,395 @@ + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 2 + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml | tee witness.flow_insensitive.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 20 + column: 5 + function: producer + updates: + - variable: m_locked + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 23 + column: 5 + function: producer + updates: + - variable: m_locked + value: "0" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 34 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + updates: + - variable: m_locked + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 38 + column: 3 + function: main + updates: + - variable: m_locked + value: "0" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m_locked || used == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= used && used <= 1)' + type: assertion + format: C + +Flow-insensitive invariants as location invariants. + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' --set witness.invariant.flow_insensitive-as location_invariant 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml > witness.location.yml + + $ diff witness.flow_insensitive.yml witness.location.yml + 67,68c67,74 + < - entry_type: flow_insensitive_invariant + < flow_insensitive_invariant: + --- + > - entry_type: location_invariant + > location: + > file_name: 74-mutex.c + > file_hash: $FILE_HASH + > line: 36 + > column: 3 + > function: main + > location_invariant: + 72,73c78,85 + < - entry_type: flow_insensitive_invariant + < flow_insensitive_invariant: + --- + > - entry_type: location_invariant + > location: + > file_name: 74-mutex.c + > file_hash: $FILE_HASH + > line: 36 + > column: 3 + > function: main + > location_invariant: + [1] + +Should also work with earlyglobs. +Earlyglobs shouldn't cause protected writes in multithreaded mode from being immediately published to protected invariant. + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable exp.earlyglobs 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + +Same with ghost_instrumentation and invariant_set entries. + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' --set witness.invariant.flow_insensitive-as invariant_set-location_invariant 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Witness] witness generation summary: + location invariants: 2 + loop invariants: 0 + flow-insensitive invariants: 0 + total generation entries: 2 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 20 + column: 5 + function: producer + updates: + - variable: m_locked + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 23 + column: 5 + function: producer + updates: + - variable: m_locked + value: "0" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 34 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + updates: + - variable: m_locked + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 38 + column: 3 + function: main + updates: + - variable: m_locked + value: "0" + format: c_expression + - entry_type: invariant_set + content: + - invariant: + type: location_invariant + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + value: '! multithreaded || (0 <= used && used <= 1)' + format: c_expression + - invariant: + type: location_invariant + location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + value: '! multithreaded || (m_locked || used == 0)' + format: c_expression + +Same with mutex-meet. + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 2 + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 20 + column: 5 + function: producer + updates: + - variable: m_locked + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 23 + column: 5 + function: producer + updates: + - variable: m_locked + value: "0" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 34 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: main + updates: + - variable: m_locked + value: "1" + format: c_expression + - location: + file_name: 74-mutex.c + file_hash: $FILE_HASH + line: 38 + column: 3 + function: main + updates: + - variable: m_locked + value: "0" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m_locked || used == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= used && used <= 1)' + type: assertion + format: C + +Should also work with earlyglobs. + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable exp.earlyglobs 74-mutex.c + [Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29) + [Warning][Deadcode] Function 'producer' has dead code: + on line 26 (74-mutex.c:26-26) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 1 + total lines: 15 + [Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 diff --git a/tests/regression/13-privatized/92-idx_priv.c b/tests/regression/13-privatized/92-idx_priv.c new file mode 100644 index 0000000000..ed0e8d3228 --- /dev/null +++ b/tests/regression/13-privatized/92-idx_priv.c @@ -0,0 +1,26 @@ +#include +#include + +int data; +pthread_mutex_t m[10]; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[4]); + data++; // NORACE + data--; // NORACE + pthread_mutex_unlock(&m[4]); + return NULL; +} + +int main() { + for (int i = 0; i < 10; i++) + pthread_mutex_init(&m[i], NULL); + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&m[4]); + __goblint_check(data == 0); // NORACE + pthread_mutex_unlock(&m[4]); + return 0; +} + diff --git a/tests/regression/13-privatized/92-idx_priv.t b/tests/regression/13-privatized/92-idx_priv.t new file mode 100644 index 0000000000..7b3adc5e0b --- /dev/null +++ b/tests/regression/13-privatized/92-idx_priv.t @@ -0,0 +1,45 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 92-idx_priv.c + [Success][Assert] Assertion "data == 0" will succeed (92-idx_priv.c:22:3-22:29) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 14 + dead: 0 + total lines: 14 + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 1 + total generation entries: 2 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 92-idx_priv.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= data && data <= 1)' + type: assertion + format: C + +TODO: protected invariant with m_4_locked without making 56-witness/68-ghost-ambiguous-idx unsound diff --git a/tests/regression/13-privatized/dune b/tests/regression/13-privatized/dune index 23c0dd3290..9227128b15 100644 --- a/tests/regression/13-privatized/dune +++ b/tests/regression/13-privatized/dune @@ -1,2 +1,6 @@ (cram (deps (glob_files *.c))) + +(cram + (applies_to 04-priv_multi) + (enabled_if (<> %{system} macosx))) 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; +} diff --git a/tests/regression/29-svcomp/16-atomic_priv.t b/tests/regression/29-svcomp/16-atomic_priv.t new file mode 100644 index 0000000000..1662f881b7 --- /dev/null +++ b/tests/regression/29-svcomp/16-atomic_priv.t @@ -0,0 +1,111 @@ + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection-atomic --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 16-atomic_priv.c + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:12:3-12:33) + [Success][Assert] Assertion "myglobal == 6" will succeed (16-atomic_priv.c:14:3-14:33) + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:16:3-16:33) + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:24:3-24:33) + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:26:3-26:33) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 17 + dead: 0 + total lines: 17 + [Warning][Race] Memory location myglobal (race with conf. 110): (16-atomic_priv.c:8:5-8:17) + write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:13:3-13:13) + write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:15:3-15:13) + read with [mhp:{created={[main, t_fun@16-atomic_priv.c:23:3-23:40]}}, thread:[main]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:24:3-24:33) + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 1 + total generation entries: 2 + [Info][Race] Memory locations race summary: + safe: 0 + vulnerable: 0 + unsafe: 1 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || myglobal == 5' + type: assertion + format: C + +Non-atomic privatization: + + $ goblint --enable ana.sv-comp.functions --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 16-atomic_priv.c + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:12:3-12:33) + [Success][Assert] Assertion "myglobal == 6" will succeed (16-atomic_priv.c:14:3-14:33) + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:16:3-16:33) + [Warning][Assert] Assertion "myglobal == 5" is unknown. (16-atomic_priv.c:24:3-24:33) + [Success][Assert] Assertion "myglobal == 5" will succeed (16-atomic_priv.c:26:3-26:33) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 17 + dead: 0 + total lines: 17 + [Warning][Race] Memory location myglobal (race with conf. 110): (16-atomic_priv.c:8:5-8:17) + write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:13:3-13:13) + write with [lock:{[__VERIFIER_atomic]}, thread:[main, t_fun@16-atomic_priv.c:23:3-23:40]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:15:3-15:13) + read with [mhp:{created={[main, t_fun@16-atomic_priv.c:23:3-23:40]}}, thread:[main]] (conf. 110) (exp: & myglobal) (16-atomic_priv.c:24:3-24:33) + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 2 + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 0 + vulnerable: 0 + unsafe: 1 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 16-atomic_priv.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || myglobal == 5' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || ((0 <= myglobal && myglobal <= 127) && myglobal != + 0)' + type: assertion + format: C 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/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t index 5060f505d9..2a760c0dcb 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 @@ -54,3 +57,123 @@ string: (0LL - (long long )g) + (long long )h == 0LL type: assertion format: C + + + $ goblint --enable warn.deterministic --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.sv-comp.functions --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 12-traces-min-rpb1.c --enable ana.apron.invariant.diff-box + [Warning][Assert] Assertion "g == h" is unknown. (12-traces-min-rpb1.c:27:3-27:26) + [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:16:3-16:26) + [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:29:3-29:26) + [Warning][Race] Memory location g (race with conf. 110): (12-traces-min-rpb1.c:7:5-7:10) + write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:14:3-14:8) + read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:27:3-27:26) + [Warning][Race] Memory location h (race with conf. 110): (12-traces-min-rpb1.c:8:5-8:10) + write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & h) (12-traces-min-rpb1.c:15:3-15:8) + read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & h) (12-traces-min-rpb1.c:27:3-27:26) + [Info][Race] Memory locations race summary: + safe: 0 + vulnerable: 0 + unsafe: 2 + total memory locations: 2 + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 18 + dead: 0 + total lines: 18 + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 1 + total generation entries: 2 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: A_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: A_locked + value: "1" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: + - variable: A_locked + value: "0" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 18 + column: 3 + function: t_fun + updates: + - variable: A_locked + value: "1" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 19 + column: 3 + function: t_fun + updates: + - variable: A_locked + value: "0" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 25 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + updates: + - variable: A_locked + value: "1" + format: c_expression + - location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 30 + column: 3 + function: main + updates: + - variable: A_locked + value: "0" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (A_locked || ((0LL - (long long )g) + (long long )h + >= 0LL && (long long )g - (long long )h >= 0LL))' + type: assertion + format: C 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/39-signed-overflows/11-imaxabs.c b/tests/regression/39-signed-overflows/11-imaxabs.c new file mode 100644 index 0000000000..47bd26569f --- /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); + __goblint_check(-100 < data); + int64_t result = data * data; // NOWARN + } + + if(imaxabs(data) <= 100) + { + __goblint_check(data <= 100); + __goblint_check(-100 <= data); + int64_t result = data * data; // 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; +} 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; +} 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..196ef8bc00 --- /dev/null +++ b/tests/regression/51-threadjoins/09-join-main.c @@ -0,0 +1,29 @@ +//PARAM: --set ana.activated[+] threadJoins +#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++; // NORACE + 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++; // NORACE + printf("main: %d\n", g); + + pthread_exit(NULL); // exit main thread but keep id2 alive, otherwise main returning kills id2 + return 0; +} 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..5b2c188bf5 --- /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++; // NORACE + printf("main: %d\n", g); + + pthread_exit(NULL); // exit main thread but keep id2 alive, otherwise main returning kills id2 + return 0; +} 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; +} 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/56-witness/64-ghost-multiple-protecting.c b/tests/regression/56-witness/64-ghost-multiple-protecting.c new file mode 100644 index 0000000000..a9fa3310e4 --- /dev/null +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.c @@ -0,0 +1,41 @@ +// PARAM: --set ana.activated[+] mutexGhosts +#include +#include +int g1, g2; +pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER; +// CRAM +void *t_fun(void *arg) { + pthread_mutex_lock(&m1); + pthread_mutex_lock(&m2); + g1 = 1; + g1 = 0; + pthread_mutex_unlock(&m2); + pthread_mutex_unlock(&m1); + + pthread_mutex_lock(&m1); + pthread_mutex_lock(&m2); + g2 = 1; + pthread_mutex_unlock(&m2); + pthread_mutex_lock(&m2); + g2 = 0; + pthread_mutex_unlock(&m2); + pthread_mutex_unlock(&m1); + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + /* pthread_mutex_lock(&m1); + __goblint_check(g1 == 0); // NOWARN (commented out) + __goblint_check(g2 == 0); // NOWARN (commented out) + pthread_mutex_unlock(&m1); + + pthread_mutex_lock(&m2); + __goblint_check(g1 == 0); // NOWARN (commented out) + __goblint_check(g2 == 0); // NOWARN (commented out) + pthread_mutex_unlock(&m2); */ + return 0; +} diff --git a/tests/regression/56-witness/64-ghost-multiple-protecting.t b/tests/regression/56-witness/64-ghost-multiple-protecting.t new file mode 100644 index 0000000000..cfa3995005 --- /dev/null +++ b/tests/regression/56-witness/64-ghost-multiple-protecting.t @@ -0,0 +1,508 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 64-ghost-multiple-protecting.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 3 + total generation entries: 4 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + +protection doesn't have precise protected invariant for g2. + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m1_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: m2_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 9 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 16 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 19 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 29 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || (m1_locked || g1 == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g2 && g2 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C + + $ goblint --set ana.base.privatization protection-read --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 64-ghost-multiple-protecting.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 4 + total generation entries: 5 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + +protection-read has precise protected invariant for g2. + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m1_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: m2_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 9 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 16 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 19 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 29 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || (m1_locked || g2 == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || (m1_locked || g1 == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g2 && g2 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C + + $ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 64-ghost-multiple-protecting.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 4 + total generation entries: 5 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m1_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: m2_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 9 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 16 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 19 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 64-ghost-multiple-protecting.c + file_hash: $FILE_HASH + line: 29 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || ((0 <= g2 && g2 <= 1) && g1 == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m1_locked || (g1 == 0 && g2 == 0))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g2 && g2 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C diff --git a/tests/regression/56-witness/65-ghost-ambiguous-lock.c b/tests/regression/56-witness/65-ghost-ambiguous-lock.c new file mode 100644 index 0000000000..7719c0cec4 --- /dev/null +++ b/tests/regression/56-witness/65-ghost-ambiguous-lock.c @@ -0,0 +1,44 @@ +// PARAM: --set ana.activated[+] mutexGhosts +#include +#include +// CRAM +int g1, g2; +pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m1); + g1 = 1; + g1 = 0; + pthread_mutex_unlock(&m1); + pthread_mutex_lock(&m2); + g2 = 1; + g2 = 0; + pthread_mutex_unlock(&m2); + return NULL; +} + +void fun(pthread_mutex_t *m) { + pthread_mutex_lock(m); + // what g2 can read? + pthread_mutex_unlock(m); +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_t *m; + int r; // rand + m = r ? &m1 : &m2; + + pthread_mutex_lock(m); + // what g1 can read? + pthread_mutex_unlock(m); + + if (r) + fun(&m1); + else + fun(&m2); + return 0; +} diff --git a/tests/regression/56-witness/65-ghost-ambiguous-lock.t b/tests/regression/56-witness/65-ghost-ambiguous-lock.t new file mode 100644 index 0000000000..36df2e4576 --- /dev/null +++ b/tests/regression/56-witness/65-ghost-ambiguous-lock.t @@ -0,0 +1,47 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 65-ghost-ambiguous-lock.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 23 + dead: 0 + total lines: 23 + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 2 + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 65-ghost-ambiguous-lock.c + file_hash: $FILE_HASH + line: 29 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g2 && g2 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.c b/tests/regression/56-witness/66-ghost-alloc-lock.c new file mode 100644 index 0000000000..073540b9db --- /dev/null +++ b/tests/regression/56-witness/66-ghost-alloc-lock.c @@ -0,0 +1,37 @@ +// PARAM: --set ana.activated[+] mutexGhosts --set ana.malloc.unique_address_count 1 +#include +#include + +int g1, g2; +pthread_mutex_t *m1; +pthread_mutex_t *m2; + +void *t_fun(void *arg) { + pthread_mutex_lock(m1); + g1 = 1; + g1 = 0; + pthread_mutex_unlock(m1); + pthread_mutex_lock(m2); + g2 = 1; + g2 = 0; + pthread_mutex_unlock(m2); + return NULL; +} + +int main() { + m1 = malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(m1, NULL); + m2 = malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(m2, NULL); + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(m1); + __goblint_check(g1 == 0); + pthread_mutex_unlock(m1); + pthread_mutex_lock(m2); + __goblint_check(g2 == 0); + pthread_mutex_unlock(m2); + return 0; +} diff --git a/tests/regression/56-witness/66-ghost-alloc-lock.t b/tests/regression/56-witness/66-ghost-alloc-lock.t new file mode 100644 index 0000000000..09c7c4250b --- /dev/null +++ b/tests/regression/56-witness/66-ghost-alloc-lock.t @@ -0,0 +1,151 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set ana.malloc.unique_address_count 1 --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 66-ghost-alloc-lock.c + [Success][Assert] Assertion "g1 == 0" will succeed (66-ghost-alloc-lock.c:31:3-31:27) + [Success][Assert] Assertion "g2 == 0" will succeed (66-ghost-alloc-lock.c:34:3-34:27) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 23 + dead: 0 + total lines: 23 + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 4 + total generation entries: 5 + [Info][Race] Memory locations race summary: + safe: 4 + vulnerable: 0 + unsafe: 0 + total memory locations: 4 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: alloc_m559918035_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: alloc_m861095507_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 10 + column: 3 + function: t_fun + updates: + - variable: alloc_m559918035_locked + value: "1" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: alloc_m559918035_locked + value: "0" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + updates: + - variable: alloc_m861095507_locked + value: "1" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 17 + column: 3 + function: t_fun + updates: + - variable: alloc_m861095507_locked + value: "0" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 30 + column: 3 + function: main + updates: + - variable: alloc_m559918035_locked + value: "1" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 32 + column: 3 + function: main + updates: + - variable: alloc_m559918035_locked + value: "0" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 33 + column: 3 + function: main + updates: + - variable: alloc_m861095507_locked + value: "1" + format: c_expression + - location: + file_name: 66-ghost-alloc-lock.c + file_hash: $FILE_HASH + line: 35 + column: 3 + function: main + updates: + - variable: alloc_m861095507_locked + value: "0" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (alloc_m861095507_locked || g2 == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (alloc_m559918035_locked || g1 == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g2 && g2 <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C diff --git a/tests/regression/56-witness/67-ghost-no-unlock.c b/tests/regression/56-witness/67-ghost-no-unlock.c new file mode 100644 index 0000000000..69ad571118 --- /dev/null +++ b/tests/regression/56-witness/67-ghost-no-unlock.c @@ -0,0 +1,27 @@ +// PARAM: --set ana.activated[+] mutexGhosts +#include +#include + +int g1; +pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m1); + g1 = 1; + g1 = 0; + pthread_mutex_unlock(&m1); + return NULL; +} + +int main() { + + + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + + pthread_mutex_lock(&m1); + __goblint_check(g1 == 0); + // no unlock + return 0; // there should be no ghost updates for unlock here +} diff --git a/tests/regression/56-witness/67-ghost-no-unlock.t b/tests/regression/56-witness/67-ghost-no-unlock.t new file mode 100644 index 0000000000..c49daf3e24 --- /dev/null +++ b/tests/regression/56-witness/67-ghost-no-unlock.t @@ -0,0 +1,84 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 67-ghost-no-unlock.c + [Success][Assert] Assertion "g1 == 0" will succeed (67-ghost-no-unlock.c:24:3-24:27) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 11 + dead: 0 + total lines: 11 + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 2 + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m1_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 9 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 12 + column: 3 + function: t_fun + updates: + - variable: m1_locked + value: "0" + format: c_expression + - location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 21 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 67-ghost-no-unlock.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + updates: + - variable: m1_locked + value: "1" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m1_locked || g1 == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g1 && g1 <= 1)' + type: assertion + format: C diff --git a/tests/regression/56-witness/68-ghost-ambiguous-idx.c b/tests/regression/56-witness/68-ghost-ambiguous-idx.c new file mode 100644 index 0000000000..7babbe003c --- /dev/null +++ b/tests/regression/56-witness/68-ghost-ambiguous-idx.c @@ -0,0 +1,28 @@ +#include +#include + +int data; +pthread_mutex_t m[10]; + +void *t_fun(void *arg) { + pthread_mutex_lock(&m[4]); + data++; + data--; + pthread_mutex_unlock(&m[4]); + return NULL; +} + +int main() { + for (int i = 0; i < 10; i++) + pthread_mutex_init(&m[i], NULL); + + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + int r; + int j = r ? 4 : 5; + pthread_mutex_lock(&m[r]); + __goblint_check(data == 0); // UNKNOWN! + pthread_mutex_unlock(&m[4]); + return 0; +} + diff --git a/tests/regression/56-witness/68-ghost-ambiguous-idx.t b/tests/regression/56-witness/68-ghost-ambiguous-idx.t new file mode 100644 index 0000000000..40025acf17 --- /dev/null +++ b/tests/regression/56-witness/68-ghost-ambiguous-idx.t @@ -0,0 +1,48 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 68-ghost-ambiguous-idx.c + [Warning][Assert] Assertion "data == 0" is unknown. (68-ghost-ambiguous-idx.c:24:3-24:29) + [Warning][Unknown] unlocking mutex (m[4]) which may not be held (68-ghost-ambiguous-idx.c:25:3-25:30) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 15 + dead: 0 + total lines: 15 + [Warning][Race] Memory location data (race with conf. 110): (68-ghost-ambiguous-idx.c:4:5-4:9) + write with [lock:{m[4]}, thread:[main, t_fun@68-ghost-ambiguous-idx.c:20:3-20:40]] (conf. 110) (exp: & data) (68-ghost-ambiguous-idx.c:9:3-9:9) + write with [lock:{m[4]}, thread:[main, t_fun@68-ghost-ambiguous-idx.c:20:3-20:40]] (conf. 110) (exp: & data) (68-ghost-ambiguous-idx.c:10:3-10:9) + read with [mhp:{created={[main, t_fun@68-ghost-ambiguous-idx.c:20:3-20:40]}}, thread:[main]] (conf. 110) (exp: & data) (68-ghost-ambiguous-idx.c:24:3-24:29) + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 1 + total generation entries: 2 + [Info][Race] Memory locations race summary: + safe: 0 + vulnerable: 0 + unsafe: 1 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 68-ghost-ambiguous-idx.c + file_hash: $FILE_HASH + line: 20 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= data && data <= 1)' + type: assertion + format: C diff --git a/tests/regression/56-witness/69-ghost-ptr-protection.c b/tests/regression/56-witness/69-ghost-ptr-protection.c new file mode 100644 index 0000000000..f5557f3fc8 --- /dev/null +++ b/tests/regression/56-witness/69-ghost-ptr-protection.c @@ -0,0 +1,30 @@ +// PARAM: --set ana.activated[+] mutexGhosts +// CRAM +#include +#include + +int g = 0; +int *p = &g; +pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER; + +void *t_fun(void *arg) { + int x = 10; + pthread_mutex_lock(&m2); + p = &x; + p = &g; + pthread_mutex_unlock(&m2); + return NULL; +} + +int main() { + pthread_t id; + pthread_create(&id, NULL, t_fun, NULL); + pthread_mutex_lock(&m1); + g = 1; + // m2_locked || (p == & g && *p == 0) would be violated here + __goblint_check(*p != 0); // 1 from g or 10 from x in t_fun + g = 0; + pthread_mutex_unlock(&m1); + return 0; +} diff --git a/tests/regression/56-witness/69-ghost-ptr-protection.t b/tests/regression/56-witness/69-ghost-ptr-protection.t new file mode 100644 index 0000000000..53aa7dd34a --- /dev/null +++ b/tests/regression/56-witness/69-ghost-ptr-protection.t @@ -0,0 +1,116 @@ + $ goblint --set ana.base.privatization protection --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 69-ghost-ptr-protection.c + [Success][Assert] Assertion "*p != 0" will succeed (69-ghost-ptr-protection.c:26:3-26:27) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 15 + dead: 0 + total lines: 15 + [Warning][Race] Memory location p (race with conf. 110): (69-ghost-ptr-protection.c:7:5-7:12) + write with [lock:{m2}, thread:[main, t_fun@69-ghost-ptr-protection.c:22:3-22:40]] (conf. 110) (exp: & p) (69-ghost-ptr-protection.c:14:3-14:9) + write with [lock:{m2}, thread:[main, t_fun@69-ghost-ptr-protection.c:22:3-22:40]] (conf. 110) (exp: & p) (69-ghost-ptr-protection.c:15:3-15:9) + read with [mhp:{created={[main, t_fun@69-ghost-ptr-protection.c:22:3-22:40]}}, lock:{m1}, thread:[main]] (conf. 110) (exp: & p) (69-ghost-ptr-protection.c:26:3-26:27) + [Info][Witness] witness generation summary: + location invariants: 0 + loop invariants: 0 + flow-insensitive invariants: 4 + total generation entries: 5 + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 1 + total memory locations: 3 + +Should not contain unsound flow-insensitive invariant m2_locked || (p == & g && *p == 0): + + $ yamlWitnessStrip < witness.yml + - entry_type: ghost_instrumentation + content: + ghost_variables: + - name: m1_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: m2_locked + scope: global + type: int + initial: + value: "0" + format: c_expression + - name: multithreaded + scope: global + type: int + initial: + value: "0" + format: c_expression + ghost_updates: + - location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 13 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "1" + format: c_expression + - location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 16 + column: 3 + function: t_fun + updates: + - variable: m2_locked + value: "0" + format: c_expression + - location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 22 + column: 3 + function: main + updates: + - variable: multithreaded + value: "1" + format: c_expression + - location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 23 + column: 3 + function: main + updates: + - variable: m1_locked + value: "1" + format: c_expression + - location: + file_name: 69-ghost-ptr-protection.c + file_hash: $FILE_HASH + line: 28 + column: 3 + function: main + updates: + - variable: m1_locked + value: "0" + format: c_expression + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m2_locked || ((0 <= *p && *p <= 1) && p == & g))' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (m1_locked || g == 0)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (0 <= g && g <= 1)' + type: assertion + format: C + - entry_type: flow_insensitive_invariant + flow_insensitive_invariant: + string: '! multithreaded || (*p == 10 || ((0 <= *p && *p <= 1) && p == & g))' + type: assertion + format: C diff --git a/tests/regression/56-witness/70-apron-unassume-set-tokens.c b/tests/regression/56-witness/70-apron-unassume-set-tokens.c new file mode 100644 index 0000000000..6ff07f6064 --- /dev/null +++ b/tests/regression/56-witness/70-apron-unassume-set-tokens.c @@ -0,0 +1,18 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.activated[+] unassume --set witness.yaml.unassume 70-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/70-apron-unassume-set-tokens.yml b/tests/regression/56-witness/70-apron-unassume-set-tokens.yml new file mode 100644 index 0000000000..1fdc29067c --- /dev/null +++ b/tests/regression/56-witness/70-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'' + ''70-apron-unassume-set-tokens.c''' + task: + input_files: + - 70-apron-unassume-set-tokens.c + input_file_hashes: + 70-apron-unassume-set-tokens.c: 71e40ed99b5217343d0831e293e7207e5bd30ce53f6ab73f0c1ef6ced1afcc60 + data_model: LP64 + language: C + content: + - invariant: + type: location_invariant + location: + file_name: 70-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: 70-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: 70-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: 70-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))) 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/cfg/util/dune b/tests/regression/cfg/util/dune index fb3c5e6899..8ab300b531 100644 --- a/tests/regression/cfg/util/dune +++ b/tests/regression/cfg/util/dune @@ -5,7 +5,5 @@ goblint_logs goblint_common goblint_lib ; TODO: avoid: extract LoopUnrolling and WitnessUtil node predicates from goblint_lib - fpath - goblint.sites.dune - goblint.build-info.dune) + fpath) (preprocess (pps ppx_deriving.std))) 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 diff --git a/tests/unit/dune b/tests/unit/dune index 5f0b909a77..07c87e7822 100644 --- a/tests/unit/dune +++ b/tests/unit/dune @@ -2,7 +2,7 @@ (test (name mainTest) - (libraries ounit2 qcheck-ounit goblint.std goblint.common goblint.lib goblint.constraint goblint.solver goblint.cdomain.value goblint.sites.dune goblint.build-info.dune) + (libraries ounit2 qcheck-ounit goblint.std goblint.common goblint.lib goblint.constraint goblint.solver goblint.cdomain.value) (preprocess (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson)) (flags :standard -linkall)) diff --git a/tests/util/dune b/tests/util/dune index 0e32304d4f..00ae40a0b9 100644 --- a/tests/util/dune +++ b/tests/util/dune @@ -1,11 +1,14 @@ (executables - (names yamlWitnessStrip yamlWitnessStripDiff) + (names yamlWitnessStrip yamlWitnessStripDiff multilibConfigure) (libraries batteries.unthreaded + goblint-cil goblint_std goblint_lib - yaml - goblint.sites.dune - goblint.build-info.dune) + yaml) (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..96cf9a706a --- /dev/null +++ b/tests/util/multilibConfigure.ml @@ -0,0 +1,4 @@ +open GoblintCil + +let () = + Printf.printf "%B" (Option.is_some Machdep.gcc32 && Option.is_some Machdep.gcc64) diff --git a/tests/util/yamlWitnessStripCommon.ml b/tests/util/yamlWitnessStripCommon.ml index d1656d3ddc..39bc231d72 100644 --- a/tests/util/yamlWitnessStripCommon.ml +++ b/tests/util/yamlWitnessStripCommon.ml @@ -50,6 +50,12 @@ struct let segment_strip_file_hash ({segment}: ViolationSequence.Segment.t): ViolationSequence.Segment.t = {segment = List.map waypoint_strip_file_hash segment} in + let ghost_location_update_strip_file_hash (x: GhostInstrumentation.LocationUpdate.t): GhostInstrumentation.LocationUpdate.t = + { + location = location_strip_file_hash x.location; + updates = List.sort GhostInstrumentation.Update.compare x.updates + } + in let entry_type: EntryType.t = match entry_type with | LocationInvariant x -> @@ -65,9 +71,14 @@ struct | PreconditionLoopInvariantCertificate x -> PreconditionLoopInvariantCertificate {x with target = target_strip_file_hash x.target} | InvariantSet x -> - InvariantSet {content = List.map invariant_strip_file_hash x.content} + InvariantSet {content = List.sort InvariantSet.Invariant.compare (List.map invariant_strip_file_hash x.content)} (* Sort, so order is deterministic regardless of Goblint. *) | ViolationSequence x -> ViolationSequence {content = List.map segment_strip_file_hash x.content} + | GhostInstrumentation x -> + GhostInstrumentation { (* Sort, so order is deterministic regardless of Goblint. *) + ghost_variables = List.sort GhostInstrumentation.Variable.compare x.ghost_variables; + ghost_updates = List.sort GhostInstrumentation.LocationUpdate.compare (List.map ghost_location_update_strip_file_hash x.ghost_updates); + } in {entry_type}