diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index d4fc872620..fd2c55b84e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -16,9 +16,9 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used runs-on: ${{ matrix.os }} @@ -35,13 +35,12 @@ jobs: # otherwise setup-ocaml pins non-locked dependencies # https://github.com/ocaml/setup-ocaml/issues/166 OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install dependencies diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 242acef3de..0ada04e369 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,9 +16,9 @@ jobs: strategy: matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used runs-on: ${{ matrix.os }} @@ -35,7 +35,7 @@ jobs: # otherwise setup-ocaml pins non-locked dependencies # https://github.com/ocaml/setup-ocaml/issues/166 OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} diff --git a/.github/workflows/indentation.yml b/.github/workflows/indentation.yml index e22e674301..1c788e7554 100644 --- a/.github/workflows/indentation.yml +++ b/.github/workflows/indentation.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: - 4.14.x @@ -25,7 +25,7 @@ jobs: fetch-depth: 0 - name: Set up OCaml ${{ matrix.ocaml-compiler }} - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} diff --git a/.github/workflows/locked.yml b/.github/workflows/locked.yml index 18935725ca..16655bfdc7 100644 --- a/.github/workflows/locked.yml +++ b/.github/workflows/locked.yml @@ -17,10 +17,10 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 - macos-13 ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used runs-on: ${{ matrix.os }} @@ -37,13 +37,12 @@ jobs: # otherwise setup-ocaml pins non-locked dependencies # https://github.com/ocaml/setup-ocaml/issues/166 OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install dependencies @@ -71,9 +70,9 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used runs-on: ${{ matrix.os }} @@ -87,13 +86,12 @@ jobs: # otherwise setup-ocaml pins non-locked dependencies # https://github.com/ocaml/setup-ocaml/issues/166 OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install spin @@ -114,9 +112,9 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file # don't add any other because they won't be used node-version: - 14 @@ -132,7 +130,7 @@ jobs: # otherwise setup-ocaml pins non-locked dependencies # https://github.com/ocaml/setup-ocaml/issues/166 OPAMLOCKED: locked - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} diff --git a/.github/workflows/unlocked.yml b/.github/workflows/unlocked.yml index f1bd399a17..f3fe6cc558 100644 --- a/.github/workflows/unlocked.yml +++ b/.github/workflows/unlocked.yml @@ -15,13 +15,13 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 - macos-13 ocaml-compiler: - 5.2.x - 5.1.x - 5.0.x - - ocaml-variants.4.14.0+options,ocaml-option-flambda + - ocaml-variants.4.14.2+options,ocaml-option-flambda - 4.14.x apron: - false @@ -30,9 +30,11 @@ jobs: - false include: - - os: ubuntu-latest + - os: ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 ocaml-compiler: 4.14.x z3: true + - os: macos-latest + ocaml-compiler: 4.14.x # customize name to use readable string for apron instead of just a boolean # workaround for missing ternary operator: https://github.com/actions/runner/issues/409 @@ -45,13 +47,12 @@ jobs: uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install dependencies @@ -89,10 +90,10 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 - macos-13 ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file, downgrade deps step + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file, downgrade deps step name: lower-bounds (${{ matrix.os }}, ${{ matrix.ocaml-compiler }}, downgrade) @@ -109,10 +110,9 @@ jobs: uses: ocaml/setup-ocaml@v2 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install dependencies @@ -133,7 +133,7 @@ jobs: - name: Downgrade dependencies # must specify ocaml-base-compiler again to prevent it from being downgraded # prevent num downgrade to avoid dune/jbuilder error: https://github.com/ocaml/dune/issues/5280 - run: opam install $(opam exec -- opam-0install --prefer-oldest goblint ocaml-variants.4.14.0+options ocaml-option-flambda num.1.5) + run: opam install $(opam exec -- opam-0install --prefer-oldest goblint ocaml-variants.4.14.2+options ocaml-option-flambda num.1.5) - name: Build run: ./make.sh nat @@ -187,10 +187,10 @@ jobs: fail-fast: false matrix: os: - - ubuntu-latest + - ubuntu-22.04 # https://github.com/ocaml/setup-ocaml/issues/872 - macos-13 ocaml-compiler: - - ocaml-variants.4.14.0+options,ocaml-option-flambda # matches opam lock file + - ocaml-variants.4.14.2+options,ocaml-option-flambda # matches opam lock file runs-on: ${{ matrix.os }} @@ -199,13 +199,12 @@ jobs: uses: actions/checkout@v4 - name: Set up OCaml ${{ matrix.ocaml-compiler }} - uses: ocaml/setup-ocaml@v2 + uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - opam-depext-flags: --with-test # doesn't work (https://github.com/ocaml/opam/issues/5836) - name: Install graph-easy # TODO: remove if depext --with-test works - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: sudo apt install -y libgraph-easy-perl - name: Install Goblint with test diff --git a/.zenodo.json b/.zenodo.json index 22705c2d9c..1e71573948 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -23,6 +23,11 @@ "affiliation": "Technische Universität München", "orcid": "0009-0009-9644-7475" }, + { + "name": "Holter, Karoliine", + "affiliation": "University of Tartu", + "orcid": "0009-0008-3725-4131" + }, { "name": "Vogler, Ralf", "affiliation": "Technische Universität München" diff --git a/CHANGELOG.md b/CHANGELOG.md index d285480259..420cc7145e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## v2.4.0 +* Remove unmaintained analyses: spec, file (#1281). +* Add linear two-variable equalities analysis (#1297, #1412, #1466). +* Add callstring, loopfree callstring and context gas analyses (#1038, #1340, #1379, #1427, #1439). +* Add non-relational thread-modular value analyses with thread IDs (#1366, #1398, #1399). +* Add NULL byte array domain (#1076). +* Fix spurious overflow warnings from internal evaluations (#1406, #1411, #1511). +* Refactor non-definite mutex handling to fix unsoundness (#1430, #1500, #1503, #1409). +* Fix non-relational thread-modular value analysis unsoundness with ambiguous points-to sets (#1457, #1458). +* Fix mutex type analysis unsoundness and enable it by default (#1414, #1416, #1510). +* Add points-to set refinement on mutex path splitting (#1287, #1343, #1374, #1396, #1407). +* Improve narrowing operators (#1502, #1540, #1543). +* Extract automatic configuration tuning for soundness (#1369). +* Fix many locations in witnesses (#1355, #1372, #1400, #1403). +* Improve output readability (#1294, #1312, #1405, #1497). +* Refactor logging (#1117). +* Modernize all library function specifications (#1029, #688, #1174, #1289, #1447, #1487). +* Remove OCaml 4.10, 4.11, 4.12 and 4.13 support (#1448). + ## v2.3.0 Functionally equivalent to Goblint in SV-COMP 2024. diff --git a/CITATION.cff b/CITATION.cff index 25d46cf762..7a93859c54 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -21,6 +21,10 @@ authors: # same authors as in .zenodo.json and dune-project family-names: Tilscher affiliation: "Technische Universität München" orcid: "https://orcid.org/0009-0009-9644-7475" + - given-names: Karoliine + family-names: Holter + affiliation: "University of Tartu" + orcid: "https://orcid.org/0009-0008-3725-4131" - given-names: Ralf family-names: Vogler affiliation: "Technische Universität München" diff --git a/bench/basic/benchSet.ml b/bench/basic/benchSet.ml index 14eb03be82..ae38c156da 100644 --- a/bench/basic/benchSet.ml +++ b/bench/basic/benchSet.ml @@ -73,7 +73,7 @@ let () = ] ); "const" @> lazy ( - let args = ((fun x -> 42), set1) in + let args = ((fun _ -> 42), set1) in throughputN 1 [ ("map1", map1, args); ("map2", map2, args); diff --git a/conf/examples/medium-program.json b/conf/examples/medium-program.json index 2c1e7c7346..5afc1aa2f8 100644 --- a/conf/examples/medium-program.json +++ b/conf/examples/medium-program.json @@ -9,6 +9,7 @@ "base", "threadid", "threadflag", + "threadreturn", "mallocWrapper", "mutexEvents", "mutex", @@ -18,6 +19,7 @@ "expRelation", "mhp", "assert", + "pthreadMutexType", "var_eq", "symb_locks", "region", diff --git a/conf/examples/very-precise.json b/conf/examples/very-precise.json index 2197335eaf..074d448a85 100644 --- a/conf/examples/very-precise.json +++ b/conf/examples/very-precise.json @@ -22,6 +22,7 @@ "base", "threadid", "threadflag", + "threadreturn", "mallocWrapper", "mutexEvents", "mutex", @@ -31,6 +32,7 @@ "expRelation", "mhp", "assert", + "pthreadMutexType", "var_eq", "symb_locks", "region", diff --git a/conf/svcomp-validate.json b/conf/svcomp-validate.json new file mode 100644 index 0000000000..f0e99057d1 --- /dev/null +++ b/conf/svcomp-validate.json @@ -0,0 +1,122 @@ +{ + "ana": { + "sv-comp": { + "enabled": true, + "functions": true + }, + "int": { + "def_exc": true, + "enums": false, + "interval": true + }, + "float": { + "interval": true, + "evaluate_math_functions": true + }, + "activated": [ + "base", + "threadid", + "threadflag", + "threadreturn", + "mallocWrapper", + "mutexEvents", + "mutex", + "access", + "race", + "escape", + "expRelation", + "mhp", + "assert", + "var_eq", + "symb_locks", + "region", + "thread", + "threadJoins", + "abortUnless", + "unassume" + ], + "path_sens": [ + "mutex", + "malloc_null", + "uninit", + "expsplit", + "activeSetjmp", + "memLeak", + "threadflag" + ], + "context": { + "widen": false + }, + "base": { + "arrays": { + "domain": "partitioned" + } + }, + "race": { + "free": false, + "call": false + }, + "autotune": { + "enabled": true, + "activated": [ + "singleThreaded", + "mallocWrappers", + "noRecursiveIntervals", + "enums", + "congruence", + "octagon", + "wideningThresholds", + "loopUnrollHeuristic", + "memsafetySpecification", + "termination", + "tmpSpecialAnalysis" + ] + }, + "widen": { + "tokens": true + } + }, + "exp": { + "region-offsets": true + }, + "solver": "td3", + "sem": { + "unknown_function": { + "spawn": false + }, + "int": { + "signed_overflow": "assume_none" + }, + "null-pointer": { + "dereference": "assume_none" + } + }, + "witness": { + "graphml": { + "enabled": false + }, + "yaml": { + "enabled": false, + "strict": true, + "format-version": "2.0", + "entry-types": [ + "location_invariant", + "loop_invariant", + "invariant_set", + "violation_sequence" + ], + "invariant-types": [ + "location_invariant", + "loop_invariant" + ] + }, + "invariant": { + "loop-head": true, + "after-lock": true, + "other": true + } + }, + "pre": { + "enabled": false + } +} diff --git a/conf/svcomp.json b/conf/svcomp.json index d2bea96040..7b8d58e790 100644 --- a/conf/svcomp.json +++ b/conf/svcomp.json @@ -46,27 +46,6 @@ "context": { "widen": false }, - "malloc": { - "wrappers": [ - "kmalloc", - "__kmalloc", - "usb_alloc_urb", - "__builtin_alloca", - "kzalloc", - - "ldv_malloc", - - "kzalloc_node", - "ldv_zalloc", - "kmalloc_array", - "kcalloc", - - "ldv_xmalloc", - "ldv_xzalloc", - "ldv_calloc", - "ldv_kzalloc" - ] - }, "base": { "arrays": { "domain": "partitioned" diff --git a/docs/developer-guide/releasing.md b/docs/developer-guide/releasing.md index d875c0d3bf..7530d9ad20 100644 --- a/docs/developer-guide/releasing.md +++ b/docs/developer-guide/releasing.md @@ -24,6 +24,8 @@ All changes must be committed because the working tree is not checked. + The warning `[FAIL] opam field doc cannot be parsed by dune-release` is fine and can be ignored (see ). + 8. Check that "unlocked" workflow passes on GitHub Actions. It can be run manually on the release branch for checking. @@ -36,12 +38,13 @@ 1. Pull Docker image: `docker pull ocaml/opam:ubuntu-22.04-ocaml-4.14` (or newer). 2. Extract distribution archive. 3. Run Docker container in extracted directory: `docker run -it --rm -v $(pwd):/goblint ocaml/opam:ubuntu-22.04-ocaml-4.14` (or newer). - 4. Navigate to distribution archive inside Docker container: `cd /goblint`. - 5. Install and test package from distribution archive: `opam-2.1 install --with-test .`. - 6. Activate opam environment: `eval $(opam env)`. - 7. Check version: `goblint --version`. - 8. Check that analysis works: `goblint -v tests/regression/04-mutex/01-simple_rc.c`. - 9. Exit Docker container. + 4. Update opam-repository from git: `opam-2.1 repository add git git+https://github.com/ocaml/opam-repository.git && opam-2.1 update`. + 5. Navigate to distribution archive inside Docker container: `cd /goblint`. + 6. Install and test package from distribution archive: `opam-2.1 install --with-test .`. + 7. Activate opam environment: `eval $(opam env)`. + 8. Check version: `goblint --version`. + 9. Check that analysis works: `goblint -v tests/regression/04-mutex/01-simple_rc.c`. + 10. Exit Docker container. 12. Temporarily enable Zenodo GitHub webhook. diff --git a/docs/developer-guide/testing.md b/docs/developer-guide/testing.md index 22b0070fa4..9444a96007 100644 --- a/docs/developer-guide/testing.md +++ b/docs/developer-guide/testing.md @@ -69,6 +69,19 @@ Other useful constructs are the following: | `__goblint_check(1); // reachable` | Checks that the line is reachable according
to Goblint results (soundness). | | `__goblint_check(0); // NOWARN (unreachable)` | Checks that the line is unreachable (precision). | +#### Meta +Comments at the end of lines can also indicate metaproperties: + +| Annotation | Expected result/comment | +| ---------- | ----- | +| `NOCRASH` | No analyzer crash | +| `FIXPOINT` | No fixpoint error | +| `NOTIMEOUT` | Analyer terminates | +| `CRAM` | Automatic checks are only in corresponding Cram test | + +These comments only document the intention of the test (if there are no other checks in the test). +Analyzer crash, fixpoint error and non-termination are checked even when there are other checks. + ## Cram Tests [Cram-style tests](https://dune.readthedocs.io/en/stable/tests.html#cram-tests) are also used to verify that existing functionality hasn't been broken. They check the complete standard output of running the Goblint binary with specified command-line arguments. diff --git a/docs/user-guide/assumptions.md b/docs/user-guide/assumptions.md index f77e3b5097..33284ccc00 100644 --- a/docs/user-guide/assumptions.md +++ b/docs/user-guide/assumptions.md @@ -17,3 +17,22 @@ _NB! This list is likely incomplete._ See [PR #1414](https://github.com/goblint/analyzer/pull/1414). +2. Pointer arithmetic does not overflow. + + [C11's N1570][n1570] at 6.5.6.8 states that + + > When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. + > [...] + > the evaluation shall not produce an overflow; otherwise, the behavior is undefined. + + after a long list of defined behaviors. + + Goblint does not report overflow and out-of-bounds pointer arithmetic (when the pointer _is not dereferenced_). + This affects the overflow analysis (SV-COMP no-overflow property) in the `base` analysis. + + This _does not_ affect the `memOutOfBounds` analysis (SV-COMP valid-memsafety property), which is for undefined behavior from _dereferencing_ such out-of-bounds pointers. + + See [PR #1511](https://github.com/goblint/analyzer/pull/1511). + + +[n1570]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf diff --git a/dune-project b/dune-project index 878abd3b4f..54915cf964 100644 --- a/dune-project +++ b/dune-project @@ -15,23 +15,38 @@ (source (github goblint/analyzer)) (homepage "https://goblint.in.tum.de") (documentation "https://goblint.readthedocs.io/en/latest/") -(authors "Simmo Saan" "Michael Schwarz" "Julian Erhard" "Sarah Tilscher" "Ralf Vogler" "Kalmer Apinis" "Vesal Vojdani" ) ; same authors as in .zenodo.json and CITATION.cff +(authors "Simmo Saan" "Michael Schwarz" "Julian Erhard" "Sarah Tilscher" "Karoliine Holter" "Ralf Vogler" "Kalmer Apinis" "Vesal Vojdani" ) ; same authors as in .zenodo.json and CITATION.cff (maintainers "Simmo Saan " "Michael Schwarz " "Karoliine Holter") (license MIT) (package (name goblint) (synopsis "Static analysis framework for C") + (description "\ +Goblint is a sound static analysis framework for C programs using abstract interpretation. +It specializes in thread-modular verification of multi-threaded programs, especially regarding data races. +Goblint includes analyses for assertions, overflows, deadlocks, etc and can be extended with new analyses. +") + (tags ( + "program analysis" + "program verification" + "static analysis" + "abstract interpretation" + "C" + "data race analysis" + "concurrency")) (depends (ocaml (>= 4.14)) - (goblint-cil (>= 2.0.3)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. + (goblint-cil (>= 2.0.4)) ; TODO no way to define as pin-depends? Used goblint.opam.template to add it for now. https://github.com/ocaml/dune/issues/3231. Alternatively, removing this line and adding cil as a git submodule and `(vendored_dirs cil)` as ./dune also works. This way, no more need to reinstall the pinned cil opam package on changes. However, then cil is cleaned and has to be rebuild together with goblint. (batteries (>= 3.5.1)) (zarith (>= 1.10)) (yojson (>= 2.0.0)) (qcheck-core (>= 0.19)) - ppx_deriving + (ppx_deriving (>= 6.0.2)) (ppx_deriving_hash (>= 0.1.2)) (ppx_deriving_yojson (>= 3.7.0)) + (ppx_blob (>= 0.8.0)) + (ppxlib (>= 0.30.0)) ; ppx_easy_deriving (ounit2 :with-test) (qcheck-ounit :with-test) (odoc :with-doc) @@ -54,7 +69,7 @@ conf-gcc ; ensures opam-repository CI installs real gcc from homebrew on MacOS ) (depopts - apron + (apron (>= v0.9.15)) z3 ) (conflicts diff --git a/goblint.opam b/goblint.opam index 692625c965..44e5ccd2c2 100644 --- a/goblint.opam +++ b/goblint.opam @@ -1,6 +1,11 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "Static analysis framework for C" +description: """ +Goblint is a sound static analysis framework for C programs using abstract interpretation. +It specializes in thread-modular verification of multi-threaded programs, especially regarding data races. +Goblint includes analyses for assertions, overflows, deadlocks, etc and can be extended with new analyses. +""" maintainer: [ "Simmo Saan " "Michael Schwarz " @@ -11,25 +16,37 @@ authors: [ "Michael Schwarz" "Julian Erhard" "Sarah Tilscher" + "Karoliine Holter" "Ralf Vogler" "Kalmer Apinis" "Vesal Vojdani" ] license: "MIT" +tags: [ + "program analysis" + "program verification" + "static analysis" + "abstract interpretation" + "C" + "data race analysis" + "concurrency" +] homepage: "https://goblint.in.tum.de" doc: "https://goblint.readthedocs.io/en/latest/" bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ "dune" {>= "3.7"} "ocaml" {>= "4.14"} - "goblint-cil" {>= "2.0.3"} + "goblint-cil" {>= "2.0.4"} "batteries" {>= "3.5.1"} "zarith" {>= "1.10"} "yojson" {>= "2.0.0"} "qcheck-core" {>= "0.19"} - "ppx_deriving" + "ppx_deriving" {>= "6.0.2"} "ppx_deriving_hash" {>= "0.1.2"} "ppx_deriving_yojson" {>= "3.7.0"} + "ppx_blob" {>= "0.8.0"} + "ppxlib" {>= "0.30.0"} "ounit2" {with-test} "qcheck-ounit" {with-test} "odoc" {with-doc} @@ -51,7 +68,10 @@ depends: [ "benchmark" {with-test} "conf-gcc" ] -depopts: ["apron" "z3"] +depopts: [ + "apron" {>= "v0.9.15"} + "z3" +] conflicts: [ "result" {< "1.5"} "ez-conf-lib" {= "1"} @@ -75,12 +95,13 @@ build: [ dev-repo: "git+https://github.com/goblint/analyzer.git" # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! -available: os-distribution != "alpine" & arch != "arm64" +available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed - [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] - # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] + [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" ] + # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release + [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] + # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release + [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} @@ -88,3 +109,7 @@ depexts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +x-ci-accept-failures: [ + "macos-homebrew" # newer MacOS headers cannot be parsed (https://github.com/ocaml/opam-repository/pull/26307#issuecomment-2258080206) + "opensuse-tumbleweed" # not GNU diff, so some cram tests fail (https://discuss.ocaml.org/t/opensuse-and-opam-tests/14641/2) +] diff --git a/goblint.opam.locked b/goblint.opam.locked index f8de683948..5f01f7915a 100644 --- a/goblint.opam.locked +++ b/goblint.opam.locked @@ -12,6 +12,7 @@ authors: [ "Michael Schwarz" "Julian Erhard" "Sarah Tilscher" + "Karoliine Holter" "Ralf Vogler" "Kalmer Apinis" "Vesal Vojdani" @@ -21,89 +22,95 @@ homepage: "https://goblint.in.tum.de" doc: "https://goblint.readthedocs.io/en/latest/" bug-reports: "https://github.com/goblint/analyzer/issues" depends: [ - "angstrom" {= "0.15.0"} - "apron" {= "v0.9.14~beta.2"} + "angstrom" {= "0.16.0"} + "apron" {= "v0.9.15"} "arg-complete" {= "0.1.0"} "astring" {= "0.8.5"} "base-bigarray" {= "base"} "base-bytes" {= "base"} "base-threads" {= "base"} "base-unix" {= "base"} - "batteries" {= "3.6.0"} + "batteries" {= "3.8.0"} "benchmark" {= "1.6" & with-test} "bigarray-compat" {= "1.1.0"} - "bigstringaf" {= "0.9.0"} + "bigstringaf" {= "0.9.1"} "bos" {= "0.2.1"} - "camlidl" {= "1.11"} + "camlidl" {= "1.12"} "camlp-streams" {= "5.0.1"} "catapult" {= "0.2"} "catapult-file" {= "0.2"} - "cmdliner" {= "1.1.1" & with-doc} - "conf-autoconf" {= "0.1"} + "cmdliner" {= "1.3.0" & with-doc} + "conf-autoconf" {= "0.2"} + "conf-findutils" {= "1"} "conf-gcc" {= "1.0"} "conf-gmp" {= "4"} - "conf-mpfr" {= "3"} + "conf-gmp-paths" {= "1"} + "conf-mpfr-paths" {= "1"} "conf-perl" {= "2"} - "conf-pkg-config" {= "2"} "conf-ruby" {= "1.0.0" & with-test} - "conf-which" {= "1"} "cppo" {= "1.6.9"} "cpu" {= "2.0.0"} - "csexp" {= "1.5.1"} - "ctypes" {= "0.20.1"} - "dune" {= "3.7.1"} - "dune-build-info" {= "3.7.1"} - "dune-configurator" {= "3.7.1"} - "dune-private-libs" {= "3.7.1"} - "dune-site" {= "3.7.1"} - "dyn" {= "3.7.1"} + "crunch" {= "3.3.1" & with-doc} + "csexp" {= "1.5.2"} + "cstruct" {= "6.2.0"} + "ctypes" {= "0.22.0"} + "dune" {= "3.16.0"} + "dune-build-info" {= "3.16.0"} + "dune-configurator" {= "3.16.0"} + "dune-private-libs" {= "3.16.0"} + "dune-site" {= "3.16.0"} + "dyn" {= "3.16.0"} + "ez-conf-lib" {= "2"} "fileutils" {= "0.6.4"} "fmt" {= "0.9.0"} "fpath" {= "0.7.3"} - "goblint-cil" {= "2.0.3"} + "goblint-cil" {= "2.0.4"} + "hex" {= "1.5.0"} "integers" {= "0.7.0"} - "json-data-encoding" {= "0.12.1"} - "jsonrpc" {= "1.15.0~5.0preview1"} + "json-data-encoding" {= "1.0.1"} + "jsonrpc" {= "1.17.0"} "logs" {= "0.7.0"} - "mlgmpidl" {= "1.2.15"} - "num" {= "1.4"} - "ocaml" {= "4.14.0"} + "mlgmpidl" {= "1.3.0"} + "num" {= "1.5"} + "ocaml" {= "4.14.2"} "ocaml-compiler-libs" {= "v0.12.4"} "ocaml-config" {= "2"} "ocaml-option-flambda" {= "1"} "ocaml-syntax-shims" {= "1.0.0"} - "ocaml-variants" {= "4.14.0+options"} - "ocamlbuild" {= "0.14.2"} - "ocamlfind" {= "1.9.5"} - "odoc" {= "2.2.0" & with-doc} - "odoc-parser" {= "2.0.0" & with-doc} - "ordering" {= "3.7.1"} - "ounit2" {= "2.2.6" & with-test} - "pp" {= "1.1.2"} + "ocaml-variants" {= "4.14.2+options"} + "ocamlbuild" {= "0.14.3"} + "ocamlfind" {= "1.9.6"} + "odoc" {= "2.4.2" & with-doc} + "odoc-parser" {= "2.4.2" & with-doc} + "ordering" {= "3.16.0"} + "ounit2" {= "2.2.7" & with-test} + "pp" {= "1.2.0"} + "ppx_blob" {= "0.9.0"} "ppx_derivers" {= "1.2.1"} - "ppx_deriving" {= "5.2.1"} + "ppx_deriving" {= "6.0.2"} "ppx_deriving_hash" {= "0.1.2"} - "ppx_deriving_yojson" {= "3.7.0"} - "ppxlib" {= "0.28.0"} - "qcheck-core" {= "0.20"} - "qcheck-ounit" {= "0.20" & with-test} - "re" {= "1.10.4" & with-doc} - "result" {= "1.5"} + "ppx_deriving_yojson" {= "3.8.0"} + "ppxlib" {= "0.32.1"} + "ptime" {= "1.1.0" & with-doc} + "qcheck-core" {= "0.21.3"} + "qcheck-ounit" {= "0.21.3" & with-test} + "re" {= "1.11.0" & with-doc} + "result" {= "1.5" & with-doc} "rresult" {= "0.7.0"} "seq" {= "base"} - "sexplib0" {= "v0.15.1"} - "sha" {= "1.15.2"} + "sexplib0" {= "v0.16.0"} + "sha" {= "1.15.4"} "stdlib-shims" {= "0.3.0"} - "stdune" {= "3.7.1"} + "stdune" {= "3.16.0"} "stringext" {= "1.6.0"} - "topkg" {= "1.0.6"} - "tyxml" {= "4.5.0" & with-doc} - "uri" {= "4.2.0"} + "topkg" {= "1.0.7"} + "tyxml" {= "4.6.0" & with-doc} + "uri" {= "4.4.0"} "uuidm" {= "0.9.8"} "uutf" {= "1.0.3" & with-doc} - "yaml" {= "3.1.0"} - "yojson" {= "2.0.2"} - "zarith" {= "1.12"} + "yaml" {= "3.2.0"} + "yojson" {= "2.2.1"} + "zarith" {= "1.13"} ] build: [ ["dune" "subst"] {dev} @@ -122,7 +129,7 @@ build: [ ["dune" "install" "-p" name "--create-install-files" name] ] dev-repo: "git+https://github.com/goblint/analyzer.git" -available: os-distribution != "alpine" & arch != "arm64" +available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") conflicts: [ "result" {< "1.5"} "ez-conf-lib" {= "1"} @@ -130,15 +137,31 @@ conflicts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] -# TODO: manually reordered to avoid opam pin crash: https://github.com/ocaml/opam/issues/4936 pin-depends: [ [ - "goblint-cil.2.0.3" - "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" + "goblint-cil.2.0.4" + "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" + ] + [ + "camlidl.1.12" + "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] [ - "ppx_deriving.5.2.1" - "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" + "apron.v0.9.15" + "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] ] depexts: ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} +description: """\ +Goblint is a sound static analysis framework for C programs using abstract interpretation. +It specializes in thread-modular verification of multi-threaded programs, especially regarding data races. +Goblint includes analyses for assertions, overflows, deadlocks, etc and can be extended with new analyses.""" +tags: [ + "program analysis" + "program verification" + "static analysis" + "abstract interpretation" + "C" + "data race analysis" + "concurrency" +] diff --git a/goblint.opam.template b/goblint.opam.template index 2d5ef10bc9..0a517fbfa0 100644 --- a/goblint.opam.template +++ b/goblint.opam.template @@ -1,11 +1,12 @@ # on `dune build` goblint.opam will be generated from goblint.opam.template and dune-project # also remember to generate/adjust goblint.opam.locked! -available: os-distribution != "alpine" & arch != "arm64" +available: os-family != "bsd" & os-distribution != "alpine" & (arch != "arm64" | os = "macos") pin-depends: [ - # published goblint-cil 2.0.3 is currently up-to-date, so no pin needed - [ "goblint-cil.2.0.3" "git+https://github.com/goblint/cil.git#ae3a4949d478fad77e004c6fe15a7c83427df59f" ] - # TODO: add back after release, only pinned for optimization (https://github.com/ocaml-ppx/ppx_deriving/pull/252) - [ "ppx_deriving.5.2.1" "git+https://github.com/ocaml-ppx/ppx_deriving.git#0a89b619f94cbbfc3b0fb3255ab4fe5bc77d32d6" ] + [ "goblint-cil.2.0.4" "git+https://github.com/goblint/cil.git#9f4fac450c02bc61a13717784515056b185794cd" ] + # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new camlidl release + [ "camlidl.1.12" "git+https://github.com/xavierleroy/camlidl.git#1c1e87e3f56c2c6b3226dd0af3510ef414b462d0" ] + # pinned for stability (https://github.com/goblint/analyzer/issues/1520), remove after new apron release + [ "apron.v0.9.15" "git+https://github.com/antoinemine/apron.git#418a217c7a70dae3f422678f3aaba38ae374d91a" ] ] depexts: [ ["libgraph-easy-perl"] {os-distribution = "ubuntu" & with-test} @@ -13,3 +14,7 @@ depexts: [ post-messages: [ "Do not benchmark Goblint on OCaml 5 (https://goblint.readthedocs.io/en/latest/user-guide/benchmarking/)." {ocaml:version >= "5.0.0"} ] +x-ci-accept-failures: [ + "macos-homebrew" # newer MacOS headers cannot be parsed (https://github.com/ocaml/opam-repository/pull/26307#issuecomment-2258080206) + "opensuse-tumbleweed" # not GNU diff, so some cram tests fail (https://discuss.ocaml.org/t/opensuse-and-opam-tests/14641/2) +] diff --git a/gobview b/gobview index 72abbb576b..76e42c34d3 160000 --- a/gobview +++ b/gobview @@ -1 +1 @@ -Subproject commit 72abbb576b8ffc8759dcfa31501738f6b561b05a +Subproject commit 76e42c34d36bd2ab6900efd661a972ba4824f065 diff --git a/make.sh b/make.sh index 5b55e51beb..0f76759065 100755 --- a/make.sh +++ b/make.sh @@ -8,7 +8,7 @@ opam_setup() { set -x opam init -y -a --bare $SANDBOXING # sandboxing is disabled in travis and docker opam update - opam switch -y create . --deps-only --packages=ocaml-variants.4.14.0+options,ocaml-option-flambda --locked + opam switch -y create . --deps-only --packages=ocaml-variants.4.14.2+options,ocaml-option-flambda --locked } rule() { diff --git a/scripts/creduce/privPrecCompare.sh b/scripts/creduce/privPrecCompare.sh index fdc5f9219d..2165112924 100755 --- a/scripts/creduce/privPrecCompare.sh +++ b/scripts/creduce/privPrecCompare.sh @@ -22,7 +22,7 @@ for PRIV in "${PRIVS[@]}"; do PRIVDUMP="$OUTDIR/$PRIV" LOG="$OUTDIR/$PRIV.log" rm -f $PRIVDUMP - $GOBLINTDIR/goblint --sets exp.privatization $PRIV --sets exp.priv-prec-dump $PRIVDUMP $OPTS -v --enable warn.debug &> $LOG + $GOBLINTDIR/goblint --set exp.privatization $PRIV --set exp.priv-prec-dump $PRIVDUMP $OPTS -v --enable warn.debug &> $LOG grep -F "Function definition missing" $LOG && exit 1 done diff --git a/scripts/goblint-lib-modules.py b/scripts/goblint-lib-modules.py index 017530f838..ba25a1403c 100755 --- a/scripts/goblint-lib-modules.py +++ b/scripts/goblint-lib-modules.py @@ -42,6 +42,10 @@ "Goblint_build_info", "Dune_build_info", + # ppx-s + "Ppx_deriving_printable", + "Ppx_deriving_lattice", + "MessageCategory", # included in Messages "PreValueDomain", # included in ValueDomain diff --git a/scripts/privPrecCompare.sh b/scripts/privPrecCompare.sh index c5b9f2c01e..2b09fb80b5 100755 --- a/scripts/privPrecCompare.sh +++ b/scripts/privPrecCompare.sh @@ -10,7 +10,7 @@ mkdir -p $OUTDIR for PRIV in "${PRIVS[@]}"; do echo $PRIV PRIVDUMP="$OUTDIR/$PRIV" - ./goblint --sets exp.privatization $PRIV --sets exp.priv-prec-dump $PRIVDUMP "$@" + ./goblint --set exp.privatization $PRIV --set exp.priv-prec-dump $PRIVDUMP "$@" done PRIVDUMPS=("${PRIVS[*]/#/$OUTDIR/}") # why [*] here? diff --git a/scripts/test-gobview.py b/scripts/test-gobview.py index f5961108d7..10808a9b62 100644 --- a/scripts/test-gobview.py +++ b/scripts/test-gobview.py @@ -6,6 +6,7 @@ from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options +from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from threading import Thread import subprocess @@ -17,6 +18,11 @@ # cleanup def cleanup(browser, thread): print("cleanup") + + # print messages + for entry in browser.get_log('browser'): + print(entry) + browser.close() p.kill() thread.join() @@ -35,6 +41,8 @@ def serve(): print("starting installation of browser\n") options = Options() options.add_argument('headless') +# options.set_capability("loggingPrefs", { 'browser':'ALL' }) +options.set_capability("goog:loggingPrefs", { 'browser':'ALL' }) browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()),options=options) print("finished webdriver installation \n") browser.maximize_window() diff --git a/scripts/update_suite.rb b/scripts/update_suite.rb index cc45095ae7..b21fb4b532 100755 --- a/scripts/update_suite.rb +++ b/scripts/update_suite.rb @@ -246,6 +246,8 @@ def compare_warnings check.call warnings[idx] != "race" when "nodeadlock" check.call warnings[idx] != "deadlock" + when "nocrash", "fixpoint", "notimeout", "cram", "nocheck" + check.call true end end end @@ -324,6 +326,18 @@ def parse_tests (lines) if obj =~ /#line ([0-9]+).*$/ then i = $1.to_i - 1 end + # test annotations are stored by line, use impossible line -42 for these metaproperties + if obj =~ /NOCRASH/ then + tests[-42] = "nocrash" + elsif obj =~ /FIXPOINT/ then + tests[-42] = "fixpoint" + elsif obj =~ /NOTIMEOUT/ then + tests[-42] = "notimeout" + elsif obj =~ /CRAM/ then + tests[-42] = "cram" + elsif obj =~ /NOCHECK/ then + tests[-42] = "nocheck" + end next if obj =~ /^\s*\/\// || obj =~ /^\s*\/\*([^*]|\*+[^*\/])*\*\/$/ todo << i if obj =~ /TODO|SKIP/ tests_line[i] = obj @@ -356,6 +370,7 @@ def parse_tests (lines) end end case lines[0] + # test annotations are stored by line, use impossible line -1 for these whole-program properties when /NONTERM/ tests[-1] = "nonterm" when /TERM/ @@ -364,6 +379,10 @@ def parse_tests (lines) if lines[0] =~ /TODO/ then todo << -1 end + if tests.empty? then + puts "No automatic checks in #{@id} (maybe NOCRASH/FIXPOINT/NOTIMEOUT/CRAM?)" + exit 1 + end Tests.new(self, tests, tests_line, todo) end diff --git a/src/analyses/apron/relationAnalysis.apron.ml b/src/analyses/apron/relationAnalysis.apron.ml index a42d78d71a..da14dfff1d 100644 --- a/src/analyses/apron/relationAnalysis.apron.ml +++ b/src/analyses/apron/relationAnalysis.apron.ml @@ -295,7 +295,7 @@ struct (* there should be smarter ways to do this, e.g. by keeping track of which values are written etc. ... *) (* See, e.g, Beckschulze E, Kowalewski S, Brauer J (2012) Access-based localization for octagons. Electron Notes Theor Comput Sci 287:29–40 *) (* Also, a local *) - let vname = Apron.Var.to_string var in + let vname = GobApron.Var.show var in let locals = fundec.sformals @ fundec.slocals in match List.find_opt (fun v -> VM.var_name (Local v) = vname) locals with (* TODO: optimize *) | None -> true @@ -418,7 +418,7 @@ struct in let any_local_reachable = any_local_reachable fundec reachable_from_args in let arg_vars = f.sformals |> List.filter (RD.Tracked.varinfo_tracked) |> List.map RV.arg in - if M.tracing then M.tracel "combine-rel" "relation remove vars: %a" (docList (fun v -> Pretty.text (Apron.Var.to_string v))) arg_vars; + if M.tracing then M.tracel "combine-rel" "relation remove vars: %a" (docList (GobApron.Var.pretty ())) arg_vars; RD.remove_vars_with new_fun_rel arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_rel adds them back with proper constraints *) let tainted = f_ask.f Queries.MayBeTainted in let tainted_vars = TaintPartialContexts.conv_varset tainted in @@ -756,7 +756,7 @@ struct let sync ctx reason = (* After the solver is finished, store the results (for later comparison) *) - if !AnalysisState.postsolving then begin + if !AnalysisState.postsolving && GobConfig.get_string "exp.relation.prec-dump" <> "" then begin let keep_local = GobConfig.get_bool "ana.relation.invariant.local" in let keep_global = GobConfig.get_bool "ana.relation.invariant.global" in @@ -772,7 +772,7 @@ struct PCU.RH.replace results ctx.node new_value; end; WideningTokens.with_local_side_tokens (fun () -> - Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread]) + Priv.sync (Analyses.ask_of_ctx ctx) ctx.global ctx.sideg ctx.local (reason :> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread]) ) let init marshal = diff --git a/src/analyses/apron/relationPriv.apron.ml b/src/analyses/apron/relationPriv.apron.ml index 3ecfb713d7..15df394d54 100644 --- a/src/analyses/apron/relationPriv.apron.ml +++ b/src/analyses/apron/relationPriv.apron.ml @@ -33,10 +33,10 @@ module type S = the state when following conditional guards. *) val write_global: ?invariant:bool -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> varinfo -> varinfo -> relation_components_t - val lock: Q.ask -> (V.t -> G.t) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t - val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> LockDomain.Addr.t -> relation_components_t + val lock: Q.ask -> (V.t -> G.t) -> relation_components_t -> LockDomain.MustLock.t -> relation_components_t + val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> LockDomain.MustLock.t -> relation_components_t - val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> relation_components_t + val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread] -> relation_components_t val escape: Node.t -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> EscapeDomain.EscapedVars.t -> relation_components_t val enter_multithreaded: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> relation_components_t -> relation_components_t @@ -113,10 +113,10 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation () -> + | `JoinCall _ when ConfCheck.branched_thread_creation () -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Normal | `Init | `Thread @@ -385,10 +385,10 @@ struct end | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Normal | `Init | `Thread -> @@ -483,7 +483,7 @@ struct let startstate () = () - let atomic_mutex = LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var + let atomic_mutex = LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var let get_m_with_mutex_inits ask getg m = let get_m = getg (V.mutex m) in @@ -503,7 +503,7 @@ struct in let get_mutex_inits = getg V.mutex_inits in let get_mutex_inits' = RD.keep_vars get_mutex_inits [g_var] in - if not (RD.mem_var get_mutex_inits' g_var) then (* TODO: is this just a workaround for an escape bug? https://github.com/goblint/analyzer/pull/1354/files#r1498882657 *) + if RD.mem_var get_mutex_global_g g_var && not (RD.mem_var get_mutex_inits' g_var) then (* TODO: is this just a workaround for an escape bug? https://github.com/goblint/analyzer/pull/1354/files#r1498882657 *) (* This is an escaped variable whose value was never side-effected to get_mutex_inits' *) get_mutex_global_g else @@ -589,9 +589,9 @@ struct let write_escape = write_global_internal ~skip_meet:true let lock ask getg (st: relation_components_t) m = - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in + let atomic = Param.handle_atomic && LockDomain.MustLock.equal m atomic_mutex in (* TODO: somehow actually unneeded here? *) - if not atomic && Locksets.(not (Lockset.mem m (current_lockset ask))) then ( + if not atomic && Locksets.(not (MustLockset.mem m (current_lockset ask))) then ( let rel = st.rel in let get_m = get_m_with_mutex_inits ask getg m in (* Additionally filter get_m in case it contains variables it no longer protects. E.g. in 36/22. *) @@ -604,7 +604,7 @@ struct st (* sound w.r.t. recursive lock *) let unlock ask getg sideg (st: relation_components_t) m: relation_components_t = - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in + let atomic = Param.handle_atomic && LockDomain.MustLock.equal m atomic_mutex in let rel = st.rel in if not atomic then ( let rel_side = keep_only_protected_globals ask m rel in @@ -674,10 +674,10 @@ struct end | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Normal | `Init | `Thread -> @@ -721,7 +721,7 @@ module type ClusterArg = functor (RD: RelationDomain.RD) -> sig module LRD: Lattice.S - val keep_only_protected_globals: Q.ask -> LockDomain.Addr.t -> LRD.t -> LRD.t + val keep_only_protected_globals: Q.ask -> LockDomain.MustLock.t -> LRD.t -> LRD.t val keep_global: varinfo -> LRD.t -> LRD.t val lock: RD.t -> LRD.t -> LRD.t -> RD.t @@ -980,7 +980,7 @@ struct let get_m_with_mutex_inits inits ask getg m = let get_m = get_relevant_writes ask m (G.mutex @@ getg (V.mutex m)) in - if M.tracing then M.traceli "relationpriv" "get_m_with_mutex_inits %a\n get=%a" LockDomain.Addr.pretty m LRD.pretty get_m; + if M.tracing then M.traceli "relationpriv" "get_m_with_mutex_inits %a\n get=%a" LockDomain.MustLock.pretty m LRD.pretty get_m; let r = if not inits then get_m @@ -993,7 +993,7 @@ struct if M.tracing then M.traceu "relationpriv" "-> %a" LRD.pretty r; r - let atomic_mutex = LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var + let atomic_mutex = LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var let get_mutex_global_g_with_mutex_inits inits ask getg g = let get_mutex_global_g = @@ -1106,8 +1106,8 @@ struct {rel = rel_local; priv = (W.add g w,lmust,l)} (* Keep write local as if it were protected by the atomic section. *) let lock ask getg (st: relation_components_t) m = - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in - if not atomic && Locksets.(not (Lockset.mem m (current_lockset ask))) then ( + let atomic = Param.handle_atomic && LockDomain.MustLock.equal m atomic_mutex in + if not atomic && Locksets.(not (MustLockset.mem m (current_lockset ask))) then ( let rel = st.rel in let _,lmust,l = st.priv in let lm = LLock.mutex m in @@ -1130,7 +1130,7 @@ struct RD.keep_filter oct protected let unlock ask getg sideg (st: relation_components_t) m: relation_components_t = - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (atomic_mutex) in + let atomic = Param.handle_atomic && LockDomain.MustLock.equal m atomic_mutex in let rel = st.rel in let w,lmust,l = st.priv in if not atomic then ( @@ -1230,10 +1230,10 @@ struct | `Return -> st (* TODO: implement? *) | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Normal | `Init | `Thread -> @@ -1314,7 +1314,7 @@ struct r let lock ask getg st m = - if M.tracing then M.traceli "relationpriv" "lock %a" LockDomain.Addr.pretty m; + if M.tracing then M.traceli "relationpriv" "lock %a" LockDomain.MustLock.pretty m; if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in @@ -1326,7 +1326,7 @@ struct r let unlock ask getg sideg st m = - if M.tracing then M.traceli "relationpriv" "unlock %a" LockDomain.Addr.pretty m; + if M.tracing then M.traceli "relationpriv" "unlock %a" LockDomain.MustLock.pretty m; if M.tracing then M.trace "relationpriv" "st: %a" RelComponents.pretty st; let getg x = let r = getg x in diff --git a/src/analyses/base.ml b/src/analyses/base.ml index 102e22b5a7..e429155e4d 100644 --- a/src/analyses/base.ml +++ b/src/analyses/base.ml @@ -34,8 +34,6 @@ module MainFunctor (Priv:BasePriv.S) (RVEval:BaseDomain.ExpEvaluator with type t struct include Analyses.DefaultSpec - exception Top - module Dom = BaseDomain.DomFunctor (Priv.D) (RVEval) type t = Dom.t module D = Dom @@ -171,7 +169,7 @@ struct * Abstract evaluation functions **************************************************************************) - let iDtoIdx = ID.cast_to (Cilfacade.ptrdiff_ikind ()) + let iDtoIdx x = ID.cast_to (Cilfacade.ptrdiff_ikind ()) x let unop_ID = function | Neg -> ID.neg @@ -261,6 +259,8 @@ struct (* adds n to the last offset *) let rec addToOffset n (t:typ option) = function | `Index (i, `NoOffset) -> + (* Binary operations on pointer types should not generate warnings in SV-COMP *) + GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> (* If we have arrived at the last Offset and it is an Index, we add our integer to it *) `Index(IdxDom.add i (iDtoIdx n), `NoOffset) | `Field (f, `NoOffset) -> @@ -451,7 +451,7 @@ struct else ctx.local - let sync ctx reason = sync' (reason :> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread]) ctx + let sync ctx reason = sync' (reason :> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread]) ctx let publish_all ctx reason = ignore (sync' reason ctx) @@ -1907,14 +1907,14 @@ struct in begin match current_val with | Bot -> (* current value is VD Bot *) - begin match Addr.to_mval (AD.choose lval_val) with - | Some (x,offs) -> + begin match AD.to_mval lval_val with + | [(x,offs)] -> let t = v.vtype in let iv = VD.bot_value ~varAttr:v.vattr t in (* correct bottom value for top level variable *) - if M.tracing then M.tracel "set" "init bot value: %a" VD.pretty iv; + if M.tracing then M.tracel "set" "init bot value (%a): %a" d_plaintype t VD.pretty iv; let nv = VD.update_offset (Queries.to_value_domain_ask (Analyses.ask_of_ctx ctx)) iv offs rval_val (Some (Lval lval)) lval t in (* do desired update to value *) set_savetop ~ctx ctx.local (AD.of_var v) lval_t nv ~lval_raw:lval ~rval_raw:rval (* set top-level variable to updated value *) - | None -> + | _ -> set_savetop ~ctx ctx.local lval_val lval_t rval_val ~lval_raw:lval ~rval_raw:rval end | _ -> @@ -2043,7 +2043,7 @@ struct List.map mpt exps ) - let invalidate ?(deep=true) ~ctx (st:store) (exps: exp list): store = + let invalidate ~(must: bool) ?(deep=true) ~ctx (st:store) (exps: exp list): store = if M.tracing && exps <> [] then M.tracel "invalidate" "Will invalidate expressions [%a]" (d_list ", " d_plainexp) exps; if exps <> [] then M.info ~category:Imprecise "Invalidating expressions: %a" (d_list ", " d_exp) exps; (* To invalidate a single address, we create a pair with its corresponding @@ -2070,7 +2070,15 @@ struct let vs = List.map (Tuple3.third) invalids' in M.tracel "invalidate" "Setting addresses [%a] to values [%a]" (d_list ", " AD.pretty) addrs (d_list ", " VD.pretty) vs ); - set_many ~ctx st invalids' + (* copied from set_many *) + let f (acc: store) ((lval:AD.t),(typ:Cil.typ),(value:value)): store = + let acc' = set ~ctx acc lval typ value in + if must then + acc' + else + D.join acc acc' + in + List.fold_left f st invalids' let make_entry ?(thread=false) (ctx:(D.t, G.t, C.t, V.t) Analyses.ctx) fundec args: D.t = @@ -2166,7 +2174,7 @@ struct end | _, _ when get_bool "sem.unknown_function.spawn" -> (* TODO: Remove sem.unknown_function.spawn check because it is (and should be) really done in LibraryFunctions. - But here we consider all non-ThreadCrate functions also unknown, so old-style LibraryFunctions access + But here we consider all non-ThreadCreate functions also unknown, so old-style LibraryFunctions access definitions using `Write would still spawn because they are not truly unknown functions (missing from LibraryFunctions). Need this to not have memmove spawn in SV-COMP. *) let shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = false } args in @@ -2209,8 +2217,8 @@ struct in (* TODO: what about escaped local variables? *) (* invalidate arguments and non-static globals for unknown functions *) - let st' = invalidate ~deep:false ~ctx ctx.local shallow_addrs in - invalidate ~deep:true ~ctx st' deep_addrs + let st' = invalidate ~must:false ~deep:false ~ctx ctx.local shallow_addrs in + invalidate ~must:false ~deep:true ~ctx st' deep_addrs let check_invalid_mem_dealloc ctx special_fn ptr = let has_non_heap_var = AD.exists (function @@ -2300,7 +2308,7 @@ struct let invalidate_ret_lv st = match lv with | Some lv -> if M.tracing then M.tracel "invalidate" "Invalidating lhs %a for function call %s" d_plainlval lv f.vname; - invalidate ~deep:false ~ctx st [Cil.mkAddrOrStartOf lv] + invalidate ~must:true ~deep:false ~ctx st [Cil.mkAddrOrStartOf lv] | None -> st in let addr_type_of_exp exp = @@ -2634,14 +2642,14 @@ struct | Int n when GobOption.exists (Z.equal Z.zero) (ID.to_int n) -> st | Address ret_a -> begin match eval_rv ~ctx st id with - | Thread a when ValueDomain.Threads.is_top a -> invalidate ~ctx st [ret_var] + | Thread a when ValueDomain.Threads.is_top a -> invalidate ~must:true ~ctx st [ret_var] | Thread a -> let v = List.fold VD.join (VD.bot ()) (List.map (fun x -> G.thread (ctx.global (V.thread x))) (ValueDomain.Threads.elements a)) in (* TODO: is this type right? *) set ~ctx st ret_a (Cilfacade.typeOf ret_var) v - | _ -> invalidate ~ctx st [ret_var] + | _ -> invalidate ~must:true ~ctx st [ret_var] end - | _ -> invalidate ~ctx st [ret_var] + | _ -> invalidate ~must:true ~ctx st [ret_var] in let st' = invalidate_ret_lv st' in Priv.thread_join (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) id st' diff --git a/src/analyses/basePriv.ml b/src/analyses/basePriv.ml index efa0575bf4..946b8f8cc5 100644 --- a/src/analyses/basePriv.ml +++ b/src/analyses/basePriv.ml @@ -28,10 +28,10 @@ sig * the state when following conditional guards. *) val write_global: ?invariant:bool -> Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> varinfo -> VD.t -> BaseComponents (D).t - val lock: Q.ask -> (V.t -> G.t) -> BaseComponents (D).t -> LockDomain.Addr.t -> BaseComponents (D).t - val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> LockDomain.Addr.t -> BaseComponents (D).t + val lock: Q.ask -> (V.t -> G.t) -> BaseComponents (D).t -> LockDomain.MustLock.t -> BaseComponents (D).t + val unlock: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> LockDomain.MustLock.t -> BaseComponents (D).t - val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> BaseComponents (D).t + val sync: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread] -> BaseComponents (D).t val escape: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> EscapeDomain.EscapedVars.t -> BaseComponents (D).t val enter_multithreaded: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseComponents (D).t -> BaseComponents (D).t @@ -191,7 +191,7 @@ struct let get_mutex_inits = getg V.mutex_inits in let is_in_Gm x _ = is_protected_by ask m x in let get_mutex_inits' = CPA.filter is_in_Gm get_mutex_inits in - if M.tracing then M.tracel "priv" "get_m_with_mutex_inits %a:\n get_m: %a\n get_mutex_inits: %a\n get_mutex_inits': %a" LockDomain.Addr.pretty m CPA.pretty get_m CPA.pretty get_mutex_inits CPA.pretty get_mutex_inits'; + if M.tracing then M.tracel "priv" "get_m_with_mutex_inits %a:\n get_m: %a\n get_mutex_inits: %a\n get_mutex_inits': %a" LockDomain.MustLock.pretty m CPA.pretty get_m CPA.pretty get_mutex_inits CPA.pretty get_mutex_inits'; CPA.join get_m get_mutex_inits' (** [get_m_with_mutex_inits] optimized for implementation-specialized [read_global]. *) @@ -281,7 +281,7 @@ struct cpa' *) let lock ask getg (st: BaseComponents (D).t) m = - if Locksets.(not (Lockset.mem m (current_lockset ask))) then ( + if Locksets.(not (MustLockset.mem m (current_lockset ask))) then ( let get_m = get_m_with_mutex_inits ask getg m in (* Really we want is_unprotected, but pthread_cond_wait emits unlock-lock events, where our (necessary) original context still has the mutex, @@ -292,7 +292,7 @@ struct No other privatization uses is_unprotected, so this hack is only needed here. *) let is_in_V x _ = is_protected_by ask m x && is_unprotected_without ask x m in let cpa' = CPA.filter is_in_V get_m in - if M.tracing then M.tracel "priv" "PerMutexOplusPriv.lock m=%a cpa'=%a" LockDomain.Addr.pretty m CPA.pretty cpa'; + if M.tracing then M.tracel "priv" "PerMutexOplusPriv.lock m=%a cpa'=%a" LockDomain.MustLock.pretty m CPA.pretty cpa'; {st with cpa = CPA.fold CPA.add cpa' st.cpa} ) else @@ -301,7 +301,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let is_in_Gm x _ = is_protected_by ask m x in let side_m_cpa = CPA.filter is_in_Gm st.cpa in - if M.tracing then M.tracel "priv" "PerMutexOplusPriv.unlock m=%a side_m_cpa=%a" LockDomain.Addr.pretty m CPA.pretty side_m_cpa; + if M.tracing then M.tracel "priv" "PerMutexOplusPriv.unlock m=%a side_m_cpa=%a" LockDomain.MustLock.pretty m CPA.pretty side_m_cpa; sideg (V.mutex m) side_m_cpa; st @@ -322,10 +322,10 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Return | `Normal | `Init @@ -384,14 +384,14 @@ struct cpa' *) let lock (ask: Queries.ask) getg (st: BaseComponents (D).t) m = - if Locksets.(not (Lockset.mem m (current_lockset ask))) then ( + if Locksets.(not (MustLockset.mem m (current_lockset ask))) then ( let get_m = get_m_with_mutex_inits ask getg m in (* Additionally filter get_m in case it contains variables it no longer protects. *) let is_in_Gm x _ = is_protected_by ask m x in let get_m = CPA.filter is_in_Gm get_m in let long_meet m1 m2 = CPA.long_map2 VD.meet m1 m2 in let meet = long_meet st.cpa get_m in - if M.tracing then M.tracel "priv" "LOCK %a:\n get_m: %a\n meet: %a" LockDomain.Addr.pretty m CPA.pretty get_m CPA.pretty meet; + if M.tracing then M.tracel "priv" "LOCK %a:\n get_m: %a\n meet: %a" LockDomain.MustLock.pretty m CPA.pretty get_m CPA.pretty meet; {st with cpa = meet} ) else @@ -433,10 +433,10 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Return | `Normal | `Init @@ -537,7 +537,7 @@ struct {st with cpa = cpa'; priv = (W.add x w,LMust.add lm lmust,l')} let lock (ask: Queries.ask) getg (st: BaseComponents (D).t) m = - if Locksets.(not (Lockset.mem m (current_lockset ask))) then ( + if Locksets.(not (MustLockset.mem m (current_lockset ask))) then ( let _,lmust,l = st.priv in let lm = LLock.mutex m in let get_m = get_m_with_mutex_inits (not (LMust.mem lm lmust)) ask getg m in @@ -764,7 +764,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let sideg = Wrapper.sideg ask sideg in - let atomic = Param.handle_atomic && LockDomain.Addr.equal m (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) in + let atomic = Param.handle_atomic && LockDomain.MustLock.equal m (LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var) in (* TODO: what about G_m globals in cpa that weren't actually written? *) CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_protected_by ask m x then ( (* is_in_Gm *) @@ -802,10 +802,10 @@ struct match reason with | `Join when ConfCheck.branched_thread_creation () -> branched_sync () - | `JoinCall when ConfCheck.branched_thread_creation_at_call ask -> + | `JoinCall f when ConfCheck.branched_thread_creation_at_call ask f -> branched_sync () | `Join - | `JoinCall + | `JoinCall _ | `Return | `Normal | `Init @@ -868,12 +868,12 @@ struct module GWeak = struct - include MapDomain.MapBot (Lockset) (WeakRange) + include MapDomain.MapBot (MustLockset) (WeakRange) let name () = "weak" end module GSync = struct - include MapDomain.MapBot (Lockset) (SyncRange) + include MapDomain.MapBot (MustLockset) (SyncRange) let name () = "synchronized" end module G = @@ -929,7 +929,7 @@ struct let invariant_vars ask getg st = let module VS = Set.Make (CilType.Varinfo) in let s = current_lockset ask in - Lockset.fold (fun m acc -> + MustLockset.fold (fun m acc -> GSync.fold (fun s' cpa' acc -> SyncRange.fold_sync_vars VS.add cpa' acc ) (G.sync (getg (V.mutex m))) acc @@ -1008,7 +1008,7 @@ struct let read_global ask getg (st: BaseComponents (D).t) x = let s = current_lockset ask in GWeak.fold (fun s' tm acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then ThreadMap.fold (fun t' v acc -> VD.join v acc ) tm acc @@ -1028,7 +1028,7 @@ struct let lock ask getg (st: BaseComponents (D).t) m = let s = current_lockset ask in let cpa' = GSync.fold (fun s' cpa' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then CPA.join cpa' acc else acc @@ -1037,13 +1037,13 @@ struct {st with cpa = cpa'} let unlock ask getg sideg (st: BaseComponents (D).t) m = - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let t = current_thread ask in let side_cpa = CPA.filter (fun x _ -> GWeak.fold (fun s' tm acc -> (* TODO: swap 2^M and T partitioning for lookup by t here first? *) let v = ThreadMap.find t tm in - (Lockset.mem m s' && not (VD.is_bot v)) || acc + (MustLockset.mem m s' && not (VD.is_bot v)) || acc ) (G.weak (getg (V.global x))) false ) st.cpa in @@ -1055,7 +1055,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1070,7 +1070,7 @@ struct let read_global ask getg (st: BaseComponents (D).t) x = let s = current_lockset ask in GWeak.fold (fun s' v acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then VD.join v acc else acc @@ -1087,7 +1087,7 @@ struct let lock ask getg (st: BaseComponents (D).t) m = let s = current_lockset ask in let cpa' = GSync.fold (fun s' cpa' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then CPA.join cpa' acc else acc @@ -1096,10 +1096,10 @@ struct {st with cpa = cpa'} let unlock ask getg sideg (st: BaseComponents (D).t) m = - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let side_cpa = CPA.filter (fun x _ -> GWeak.fold (fun s' v acc -> - (Lockset.mem m s' && not (VD.is_bot v)) || acc + (MustLockset.mem m s' && not (VD.is_bot v)) || acc ) (G.weak (getg (V.global x))) false ) st.cpa in @@ -1111,7 +1111,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1142,7 +1142,7 @@ struct let read_global ask getg (st: BaseComponents (D).t) x = let s = current_lockset ask in GWeak.fold (fun s' v acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then VD.join v acc else acc @@ -1163,7 +1163,7 @@ struct let lock ask getg (st: BaseComponents (D).t) m = let s = current_lockset ask in let cpa' = GSync.fold (fun s' cpa' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then CPA.join cpa' acc else acc @@ -1172,7 +1172,7 @@ struct {st with cpa = cpa'} let unlock ask getg sideg (st: BaseComponents (D).t) m = - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let is_in_W x _ = W.mem x st.priv in let side_cpa = CPA.filter is_in_W st.cpa in sideg (V.mutex m) (G.create_sync (GSync.singleton s side_cpa)); @@ -1183,7 +1183,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1193,7 +1193,7 @@ struct if Param.side_effect_global_init then ( CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_global ask x then ( - sideg (V.global x) (G.create_weak (GWeak.singleton (Lockset.empty ()) v)); + sideg (V.global x) (G.create_weak (GWeak.singleton (MustLockset.empty ()) v)); {st with priv = W.add x st.priv} (* TODO: is this add necessary? *) ) else @@ -1216,13 +1216,13 @@ struct module DV = struct - include MapDomain.MapBot_LiftTop (Lock) (MustVars) + include MapDomain.MapBot_LiftTop (LockDomain.MustLock) (MustVars) let name () = "V" end module L = struct - include MapDomain.MapBot_LiftTop (Lock) (MinLocksets) + include MapDomain.MapBot_LiftTop (LockDomain.MustLock) (MinLocksets) let name () = "L" end end @@ -1246,7 +1246,7 @@ struct let startstate () = (DV.bot (), L.bot ()) - let lockset_init = Lockset.top () + let lockset_init = MustLockset.all () let distr_init getg x v = if get_bool "exp.priv-distr-init" then @@ -1265,7 +1265,7 @@ struct let syncs = UnwrappedG.sync (getg (V.mutex m)) in MinLocksets.fold (fun b acc -> GSync.fold (fun s' cpa' acc -> - if Lockset.disjoint b s' then + if MustLockset.disjoint b s' then let v = CPA.find x cpa' in VD.join v acc else @@ -1278,7 +1278,7 @@ struct in let weaks = UnwrappedG.weak (getg (V.global x)) in let d_weak = GWeak.fold (fun s' v acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then VD.join v acc else acc @@ -1325,7 +1325,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let sideg = Wrapper.sideg ask sideg in let getg = Wrapper.getg ask getg in - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let is_in_G x _ = is_global ask x in let side_cpa = CPA.filter is_in_G st.cpa in let side_cpa = CPA.mapi (fun x v -> @@ -1342,7 +1342,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1380,13 +1380,13 @@ struct module GWeakW = struct - include MapDomain.MapBot (Lockset) (VD) + include MapDomain.MapBot (MustLockset) (VD) let fold_weak f m a = fold (fun _ v a -> f v a) m a end module GSyncW = struct - include MapDomain.MapBot (Lockset) (LockCenteredBase.CPA) + include MapDomain.MapBot (MustLockset) (LockCenteredBase.CPA) let fold_sync_vars f m a = fold (fun _ cpa a -> @@ -1418,11 +1418,11 @@ struct let startstate () = (W.bot (), P.top ()) - let lockset_init = Lockset.top () + let lockset_init = MustLockset.all () let distr_init getg x v = if get_bool "exp.priv-distr-init" then - let v_init = GWeakW.find lockset_init (GWeak.find (Lockset.empty ()) (UnwrappedG.weak (getg (V.global x)))) in + let v_init = GWeakW.find lockset_init (GWeak.find (MustLockset.empty ()) (UnwrappedG.weak (getg (V.global x)))) in VD.join v v_init else v @@ -1433,13 +1433,13 @@ struct let (w, p) = st.priv in let p_x = P.find x p in let d_cpa = CPA.find x st.cpa in - let d_sync = Lockset.fold (fun m acc -> - if MinLocksets.exists (fun s''' -> not (Lockset.mem m s''')) p_x then + let d_sync = MustLockset.fold (fun m acc -> + if MinLocksets.exists (fun s''' -> not (MustLockset.mem m s''')) p_x then let syncs = UnwrappedG.sync (getg (V.mutex m)) in GSync.fold (fun s' gsyncw' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then GSyncW.fold (fun w' cpa' acc -> - if MinLocksets.exists (fun s'' -> Lockset.disjoint s'' w') p_x then + if MinLocksets.exists (fun s'' -> MustLockset.disjoint s'' w') p_x then let v = CPA.find x cpa' in VD.join v acc else @@ -1454,9 +1454,9 @@ struct in let weaks = UnwrappedG.weak (getg (V.global x)) in let d_weak = GWeak.fold (fun s' gweakw' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then GWeakW.fold (fun w' v acc -> - if MinLocksets.exists (fun s'' -> Lockset.disjoint s'' w') p_x then + if MinLocksets.exists (fun s'' -> MustLockset.disjoint s'' w') p_x then VD.join v acc else acc @@ -1496,10 +1496,10 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let getg = Wrapper.getg ask getg in let sideg = Wrapper.sideg ask sideg in - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let (w, p) = st.priv in let p' = P.map (fun s' -> MinLocksets.add s s') p in - if M.tracing then M.traceli "priv" "unlock %a %a" Lock.pretty m CPA.pretty st.cpa; + if M.tracing then M.traceli "priv" "unlock %a %a" LockDomain.MustLock.pretty m CPA.pretty st.cpa; let side_gsyncw = CPA.fold (fun x v acc -> if is_global ask x then ( let w_x = W.find x w in @@ -1512,7 +1512,7 @@ struct acc ) st.cpa (GSyncW.bot ()) in - if M.tracing then M.traceu "priv" "unlock %a %a" Lock.pretty m GSyncW.pretty side_gsyncw; + if M.tracing then M.traceu "priv" "unlock %a %a" LockDomain.MustLock.pretty m GSyncW.pretty side_gsyncw; sideg (V.mutex m) (UnwrappedG.create_sync (GSync.singleton s side_gsyncw)); {st with priv = (w, p')} @@ -1521,7 +1521,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1533,7 +1533,7 @@ struct if EscapeDomain.EscapedVars.mem x escaped then ( let (w, p) = st.priv in let p' = P.add x (MinLocksets.singleton s) p in - sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (Lockset.empty ()) (GWeakW.singleton lockset_init v))); + sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (MustLockset.empty ()) (GWeakW.singleton lockset_init v))); {st with cpa = CPA.remove x st.cpa; priv = (w, p')} ) else @@ -1544,7 +1544,7 @@ struct let sideg = Wrapper.sideg ask sideg in CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_global ask x then ( - sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (Lockset.empty ()) (GWeakW.singleton lockset_init v))); + sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (MustLockset.empty ()) (GWeakW.singleton lockset_init v))); {st with cpa = CPA.remove x st.cpa} ) else @@ -1574,11 +1574,11 @@ struct let startstate () = ((W.bot (), P.top ()), (DV.bot (), L.bot ())) - let lockset_init = Lockset.top () + let lockset_init = MustLockset.all () let distr_init getg x v = if get_bool "exp.priv-distr-init" then - let v_init = GWeakW.find lockset_init (GWeak.find (Lockset.empty ()) (UnwrappedG.weak (getg (V.global x)))) in + let v_init = GWeakW.find lockset_init (GWeak.find (MustLockset.empty ()) (UnwrappedG.weak (getg (V.global x)))) in VD.join v v_init else v @@ -1594,9 +1594,9 @@ struct let syncs = UnwrappedG.sync (getg (V.mutex m)) in MinLocksets.fold (fun b acc -> GSync.fold (fun s' gsyncw' acc -> - if Lockset.disjoint b s' then + if MustLockset.disjoint b s' then GSyncW.fold (fun w' cpa' acc -> - if MinLocksets.exists (fun s'' -> Lockset.disjoint s'' w') p_x then + if MinLocksets.exists (fun s'' -> MustLockset.disjoint s'' w') p_x then let v = CPA.find x cpa' in VD.join v acc else @@ -1612,9 +1612,9 @@ struct in let weaks = UnwrappedG.weak (getg (V.global x)) in let d_m_weak = GWeak.fold (fun s' gweakw' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then GWeakW.fold (fun w' v acc -> - if MinLocksets.exists (fun s'' -> Lockset.disjoint s'' w') p_x then + if MinLocksets.exists (fun s'' -> MustLockset.disjoint s'' w') p_x then VD.join v acc else acc @@ -1624,13 +1624,13 @@ struct ) weaks (VD.bot ()) in let d_m = VD.join d_m_sync d_m_weak in - let d_g_sync = Lockset.fold (fun m acc -> - if MinLocksets.exists (fun s''' -> not (Lockset.mem m s''')) p_x then + let d_g_sync = MustLockset.fold (fun m acc -> + if MinLocksets.exists (fun s''' -> not (MustLockset.mem m s''')) p_x then let syncs = UnwrappedG.sync (getg (V.mutex m)) in GSync.fold (fun s' gsyncw' acc -> - if Lockset.disjoint s s' then + if MustLockset.disjoint s s' then GSyncW.fold (fun w' cpa' acc -> - if MinLocksets.exists (fun s'' -> Lockset.disjoint s'' w') p_x then + if MinLocksets.exists (fun s'' -> MustLockset.disjoint s'' w') p_x then let v = CPA.find x cpa' in VD.join v acc else @@ -1682,7 +1682,7 @@ struct let unlock ask getg sideg (st: BaseComponents (D).t) m = let getg = Wrapper.getg ask getg in let sideg = Wrapper.sideg ask sideg in - let s = Lockset.remove m (current_lockset ask) in + let s = MustLockset.remove m (current_lockset ask) in let ((w, p), vl) = st.priv in let p' = P.map (fun s' -> MinLocksets.add s s') p in let side_gsyncw = CPA.fold (fun x v acc -> @@ -1704,7 +1704,7 @@ struct | `Return | `Normal | `Join (* TODO: no problem with branched thread creation here? *) - | `JoinCall + | `JoinCall _ | `Init | `Thread -> st @@ -1716,7 +1716,7 @@ struct if EscapeDomain.EscapedVars.mem x escaped then ( let ((w, p), (vv, l)) = st.priv in let p' = P.add x (MinLocksets.singleton s) p in - sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (Lockset.empty ()) (GWeakW.singleton lockset_init v))); + sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (MustLockset.empty ()) (GWeakW.singleton lockset_init v))); {st with cpa = CPA.remove x st.cpa; priv = ((w, p'), (vv, l))} ) else @@ -1727,7 +1727,7 @@ struct let sideg = Wrapper.sideg ask sideg in CPA.fold (fun x v (st: BaseComponents (D).t) -> if is_global ask x then ( - sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (Lockset.empty ()) (GWeakW.singleton lockset_init v))); + sideg (V.global x) (UnwrappedG.create_weak (GWeak.singleton (MustLockset.empty ()) (GWeakW.singleton lockset_init v))); {st with cpa = CPA.remove x st.cpa} ) else @@ -1836,7 +1836,7 @@ struct r let lock ask getg st m = - if M.tracing then M.traceli "priv" "lock %a" LockDomain.Addr.pretty m; + if M.tracing then M.traceli "priv" "lock %a" LockDomain.MustLock.pretty m; if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let getg x = let r = getg x in @@ -1848,7 +1848,7 @@ struct r let unlock ask getg sideg st m = - if M.tracing then M.traceli "priv" "unlock %a" LockDomain.Addr.pretty m; + if M.tracing then M.traceli "priv" "unlock %a" LockDomain.MustLock.pretty m; if M.tracing then M.trace "priv" "st: %a" BaseComponents.pretty st; let getg x = let r = getg x in diff --git a/src/analyses/basePriv.mli b/src/analyses/basePriv.mli index 87b17e5ad0..edcf70ec98 100644 --- a/src/analyses/basePriv.mli +++ b/src/analyses/basePriv.mli @@ -17,10 +17,10 @@ sig val read_global: Queries.ask -> (V.t -> G.t) -> BaseDomain.BaseComponents (D).t -> varinfo -> BaseDomain.VD.t val write_global: ?invariant:bool -> Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> varinfo -> BaseDomain.VD.t -> BaseDomain.BaseComponents (D).t - val lock: Queries.ask -> (V.t -> G.t) -> BaseDomain.BaseComponents (D).t -> LockDomain.Addr.t -> BaseDomain.BaseComponents (D).t - val unlock: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> LockDomain.Addr.t -> BaseDomain.BaseComponents (D).t + val lock: Queries.ask -> (V.t -> G.t) -> BaseDomain.BaseComponents (D).t -> LockDomain.MustLock.t -> BaseDomain.BaseComponents (D).t + val unlock: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> LockDomain.MustLock.t -> BaseDomain.BaseComponents (D).t - val sync: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> [`Normal | `Join | `JoinCall | `Return | `Init | `Thread] -> BaseDomain.BaseComponents (D).t + val sync: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return | `Init | `Thread] -> BaseDomain.BaseComponents (D).t val escape: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> EscapeDomain.EscapedVars.t -> BaseDomain.BaseComponents (D).t val enter_multithreaded: Queries.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> BaseDomain.BaseComponents (D).t -> BaseDomain.BaseComponents (D).t diff --git a/src/analyses/commonPriv.ml b/src/analyses/commonPriv.ml index 1ece8dcc60..915b3da063 100644 --- a/src/analyses/commonPriv.ml +++ b/src/analyses/commonPriv.ml @@ -61,7 +61,7 @@ struct true (** Whether branched thread creation at start nodes of procedures needs to be handled by [sync `JoinCall] of privatization. *) - let branched_thread_creation_at_call (ask:Queries.ask) = + let branched_thread_creation_at_call (ask:Queries.ask) f = let threadflag_active = List.mem "threadflag" (GobConfig.get_string_list "ana.activated") in if threadflag_active then let sens = GobConfig.get_string_list "ana.ctx_sens" in @@ -74,7 +74,7 @@ struct if not threadflag_ctx_sens then true else - ask.f (Queries.GasExhausted) + ask.f (Queries.GasExhausted f) else true end @@ -110,7 +110,7 @@ module MutexGlobals = struct module VMutex = struct - include LockDomain.Addr + include LockDomain.MustLock let name () = "mutex" end module VMutexInits = Printable.UnitConf (struct let name = "MUTEX_INITS" end) @@ -148,23 +148,14 @@ end module Locksets = struct - module Lock = - struct - include LockDomain.Addr - let name () = "lock" - end - - module Lockset = SetDomain.ToppedSet (Lock) (struct let topname = "All locks" end) - - module MustLockset = SetDomain.Reverse (Lockset) + module MustLockset = LockDomain.MustLockset - let current_lockset (ask: Q.ask): Lockset.t = + let current_lockset (ask: Q.ask): MustLockset.t = (* TODO: remove this global_init workaround *) if !AnalysisState.global_initialization then - Lockset.empty () + MustLockset.empty () else - let mls = ask.f Queries.MustLockset in - LockDomain.MustLockset.fold (fun ml acc -> Lockset.add (Addr (LockDomain.MustLock.to_mval ml)) acc) mls (Lockset.empty ()) (* TODO: use MustLockset as Lockset *) + ask.f Queries.MustLockset (* TODO: reversed SetDomain.Hoare *) module MinLocksets = HoareDomain.Set_LiftTop (MustLockset) (struct let topname = "All locksets" end) (* reverse Lockset because Hoare keeps maximal, but we need minimal *) @@ -189,7 +180,7 @@ struct let name () = "P" (* TODO: change MinLocksets.exists/top instead? *) - let find x p = find_opt x p |? MinLocksets.singleton (Lockset.empty ()) (* ensure exists has something to check for thread returns *) + let find x p = find_opt x p |? MinLocksets.singleton (MustLockset.empty ()) (* ensure exists has something to check for thread returns *) end end @@ -270,7 +261,7 @@ struct module LLock = struct - include Printable.Either (Locksets.Lock) (struct include CilType.Varinfo let name () = "global" end) + include Printable.Either (LockDomain.MustLock) (struct include CilType.Varinfo let name () = "global" end) let mutex m = `Left m let global x = `Right x end @@ -333,7 +324,7 @@ let lift_lock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = match addr with | UnknownPtr -> st | Addr (v, _) when ask.f (IsMultiple v) -> st - | Addr mv when LockDomain.Mval.is_definite mv -> f st addr + | Addr mv when LockDomain.Mval.is_definite mv -> f st (LockDomain.MustLock.of_mval mv) | Addr _ | NullPtr | StrPtr _ -> st @@ -348,16 +339,16 @@ let lift_unlock (ask: Q.ask) f st (addr: LockDomain.Addr.t) = | UnknownPtr -> LockDomain.MustLockset.fold (fun ml st -> (* call privatization's unlock only with definite lock *) - f st (LockDomain.Addr.Addr (LockDomain.MustLock.to_mval ml)) (* TODO: no conversion *) + f st ml ) (ask.f MustLockset) st | StrPtr _ | NullPtr -> st - | Addr mv when LockDomain.Mval.is_definite mv -> f st addr + | Addr mv when LockDomain.Mval.is_definite mv -> f st (LockDomain.MustLock.of_mval mv) | Addr mv -> LockDomain.MustLockset.fold (fun ml st -> if LockDomain.MustLock.semantic_equal_mval ml mv = Some false then st else (* call privatization's unlock only with definite lock *) - f st (Addr (LockDomain.MustLock.to_mval ml)) (* TODO: no conversion *) + f st ml ) (ask.f MustLockset) st diff --git a/src/analyses/mCP.ml b/src/analyses/mCP.ml index e4c0e261e4..6212b6de90 100644 --- a/src/analyses/mCP.ml +++ b/src/analyses/mCP.ml @@ -318,10 +318,10 @@ struct f (Result.top ()) (!base_id, spec !base_id, assoc !base_id ctx.local) *) | Queries.DYojson -> `Lifted (D.to_yojson ctx.local) - | Queries.GasExhausted -> + | Queries.GasExhausted f -> if (get_int "ana.context.gas_value" >= 0) then (* There is a lifter above this that will answer it, save to ask *) - ctx.ask (Queries.GasExhausted) + ctx.ask (Queries.GasExhausted f) else (* Abort to avoid infinite recursion *) false diff --git a/src/analyses/mutexAnalysis.ml b/src/analyses/mutexAnalysis.ml index b44795b6da..9b6aa4f4ca 100644 --- a/src/analyses/mutexAnalysis.ml +++ b/src/analyses/mutexAnalysis.ml @@ -209,15 +209,14 @@ struct MustLockset.disjoint held_locks protecting | Queries.MayBePublicWithout _ when MustLocksetRW.is_all ls -> false | Queries.MayBePublicWithout {global=v; write; without_mutex; protection} -> - let held_locks = MustLocksetRW.to_must_lockset @@ fst @@ Arg.remove' ctx ~warn:false without_mutex in + let held_locks = MustLockset.remove without_mutex (MustLocksetRW.to_must_lockset ls) in let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if Mutexes.mem verifier_atomic (Lockset.export_locks (Lockset.remove (without_mutex, true) ctx.local)) then false else *) MustLockset.disjoint held_locks protecting - | Queries.MustBeProtectedBy {mutex = Addr mutex_mv; global=v; write; protection} when Mval.is_definite mutex_mv -> (* only definite Addrs can be in must-locksets to begin with, anything else cannot protect anything *) - let ml = LockDomain.MustLock.of_mval mutex_mv in + | Queries.MustBeProtectedBy {mutex = ml; global=v; write; protection} -> let protecting = protecting ~write protection v in (* TODO: unsound in 29/24, why did we do this before? *) (* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then diff --git a/src/analyses/mutexEventsAnalysis.ml b/src/analyses/mutexEventsAnalysis.ml index c5339e68cf..5ea0afc809 100644 --- a/src/analyses/mutexEventsAnalysis.ml +++ b/src/analyses/mutexEventsAnalysis.ml @@ -5,7 +5,6 @@ module M = Messages module Addr = ValueDomain.Addr module LF = LibraryFunctions -open Batteries open GoblintCil open Analyses open GobConfig @@ -52,12 +51,12 @@ struct let return ctx exp fundec : D.t = (* deprecated but still valid SV-COMP convention for atomic block *) - if get_bool "ana.sv-comp.functions" && String.starts_with fundec.svar.vname "__VERIFIER_atomic_" then + if get_bool "ana.sv-comp.functions" && String.starts_with fundec.svar.vname ~prefix:"__VERIFIER_atomic_" then ctx.emit (Events.Unlock (LockDomain.Addr.of_var LF.verifier_atomic_var)) let body ctx f : D.t = (* deprecated but still valid SV-COMP convention for atomic block *) - if get_bool "ana.sv-comp.functions" && String.starts_with f.svar.vname "__VERIFIER_atomic_" then + if get_bool "ana.sv-comp.functions" && String.starts_with f.svar.vname ~prefix:"__VERIFIER_atomic_" then ctx.emit (Events.Lock (LockDomain.Addr.of_var LF.verifier_atomic_var, true)) let special (ctx: (unit, _, _, _) ctx) lv f arglist : D.t = diff --git a/src/analyses/mutexTypeAnalysis.ml b/src/analyses/mutexTypeAnalysis.ml index 441f2e9953..4a993bbd7d 100644 --- a/src/analyses/mutexTypeAnalysis.ml +++ b/src/analyses/mutexTypeAnalysis.ml @@ -12,16 +12,17 @@ struct let name () = "pthreadMutexType" - (* Removing indexes here avoids complicated lookups and allows to have the LVals as vars here, at the price that different types of mutexes in arrays are not dinstinguished *) - module O = Offset.Unit - - module V = struct - include Printable.Prod(CilType.Varinfo)(O) (* TODO: use Mval.Unit *) + module V = + struct + (* Removing indexes here avoids complicated lookups and allows to have the LVals as vars here, at the price that different types of mutexes in arrays are not dinstinguished *) + include Mval.Unit let is_write_only _ = false end module G = MAttr + module O = Offset.Unit + (* transfer functions *) let assign ctx (lval:lval) (rval:exp) : D.t = match lval with diff --git a/src/analyses/symbLocks.ml b/src/analyses/symbLocks.ml index 0119f09288..ab116c525d 100644 --- a/src/analyses/symbLocks.ml +++ b/src/analyses/symbLocks.ml @@ -21,8 +21,6 @@ module Spec = struct include Analyses.DefaultSpec - exception Top - module D = SymbLocksDomain.Symbolic include Analyses.ValueContexts(D) diff --git a/src/analyses/threadReturn.ml b/src/analyses/threadReturn.ml index f3b9622b00..d72e2586e8 100644 --- a/src/analyses/threadReturn.ml +++ b/src/analyses/threadReturn.ml @@ -12,7 +12,7 @@ struct include Analyses.IdentitySpec let name () = "threadreturn" - module D = IntDomain.Booleans + module D = BoolDomain.MayBool include Analyses.ValueContexts(D) (* transfer functions *) diff --git a/src/analyses/unassumeAnalysis.ml b/src/analyses/unassumeAnalysis.ml index 265e9c6925..8f8892b8be 100644 --- a/src/analyses/unassumeAnalysis.ml +++ b/src/analyses/unassumeAnalysis.ml @@ -71,21 +71,11 @@ struct | _ -> () ); - let loc_of_location (location: YamlWitnessType.Location.t): Cil.location = { - file = location.file_name; - line = location.line; - column = location.column; - byte = -1; - endLine = -1; - endColumn = -1; - endByte = -1; - synthetic = false; - } - in - let yaml = match Yaml_unix.of_file (Fpath.v (GobConfig.get_string "witness.yaml.unassume")) with | Ok yaml -> yaml - | Error (`Msg m) -> failwith ("Yaml_unix.of_file: " ^ m) + | Error (`Msg m) -> + Logs.error "Yaml_unix.of_file: %s" m; + Svcomp.errorwith "witness missing" in let yaml_entries = yaml |> GobYaml.list |> BatResult.get_ok in @@ -123,7 +113,7 @@ struct in let unassume_location_invariant (location_invariant: YamlWitnessType.LocationInvariant.t) = - let loc = loc_of_location location_invariant.location in + let loc = YamlWitness.loc_of_location location_invariant.location in let inv = location_invariant.location_invariant.string in let msgLoc: M.Location.t = CilLocation loc in @@ -135,7 +125,7 @@ struct in let unassume_loop_invariant (loop_invariant: YamlWitnessType.LoopInvariant.t) = - let loc = loc_of_location loop_invariant.location in + let loc = YamlWitness.loc_of_location loop_invariant.location in let inv = loop_invariant.loop_invariant.string in let msgLoc: M.Location.t = CilLocation loc in @@ -185,7 +175,7 @@ struct in let unassume_precondition_loop_invariant (precondition_loop_invariant: YamlWitnessType.PreconditionLoopInvariant.t) = - let loc = loc_of_location precondition_loop_invariant.location in + let loc = YamlWitness.loc_of_location precondition_loop_invariant.location in let pre = precondition_loop_invariant.precondition.string in let inv = precondition_loop_invariant.loop_invariant.string in let msgLoc: M.Location.t = CilLocation loc in @@ -200,7 +190,7 @@ struct let unassume_invariant_set (invariant_set: YamlWitnessType.InvariantSet.t) = let unassume_location_invariant (location_invariant: YamlWitnessType.InvariantSet.LocationInvariant.t) = - let loc = loc_of_location location_invariant.location in + let loc = YamlWitness.loc_of_location location_invariant.location in let inv = location_invariant.value in let msgLoc: M.Location.t = CilLocation loc in @@ -212,7 +202,7 @@ struct in let unassume_loop_invariant (loop_invariant: YamlWitnessType.InvariantSet.LoopInvariant.t) = - let loc = loc_of_location loop_invariant.location in + let loc = YamlWitness.loc_of_location loop_invariant.location in let inv = loop_invariant.value in let msgLoc: M.Location.t = CilLocation loc in diff --git a/src/analyses/uninit.ml b/src/analyses/uninit.ml index a385d0a1cd..8c217cda4e 100644 --- a/src/analyses/uninit.ml +++ b/src/analyses/uninit.ml @@ -135,7 +135,7 @@ struct in let utar, uoth = unrollType target, unrollType other in match ofs, utar, uoth with - | `NoOffset, _ , _ when utar == uoth -> [v, rev cx] + | `NoOffset, _ , _ when CilType.Typ.equal utar uoth -> [v, rev cx] | `NoOffset, _ , TComp (c2,_) when not c2.cstruct -> (* unroll other (union) *) List.concat (List.rev_map (fun oth_f -> get_pfx v (`Field (oth_f, cx)) ofs utar oth_f.ftype) c2.cfields) diff --git a/src/analyses/varEq.ml b/src/analyses/varEq.ml index 8ece99d6e8..78013ec21d 100644 --- a/src/analyses/varEq.ml +++ b/src/analyses/varEq.ml @@ -11,8 +11,6 @@ open Analyses module Spec = struct - exception Top - include Analyses.DefaultSpec module D = diff --git a/src/autoTune.ml b/src/autoTune.ml index 434b4fb0b2..0def6021fa 100644 --- a/src/autoTune.ml +++ b/src/autoTune.ml @@ -68,6 +68,7 @@ let functionArgs fd = (ResettableLazy.force functionCallMaps).argLists |> Functi let findMallocWrappers () = let isMalloc f = + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo f) @@ fun () -> if LibraryFunctions.is_special f then ( let desc = LibraryFunctions.find f in match functionArgs f with @@ -104,7 +105,7 @@ let rec setCongruenceRecursive fd depth neigbourFunction = | exception Not_found -> () (* Happens for __goblint_bounded *) ) (FunctionSet.filter (*for extern and builtin functions there is no function definition in CIL*) - (fun x -> not (isExtern x.vstorage || BatString.starts_with x.vname "__builtin")) + (fun x -> not (isExtern x.vstorage || String.starts_with x.vname ~prefix:"__builtin")) (neigbourFunction fd.svar) ) ; @@ -153,6 +154,7 @@ let disableIntervalContextsInRecursiveFunctions () = let hasFunction pred = let relevant_static var = + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo var) @@ fun () -> if LibraryFunctions.is_special var then let desc = LibraryFunctions.find var in GobOption.exists (fun args -> pred (desc.special args)) (functionArgs var) @@ -160,6 +162,7 @@ let hasFunction pred = false in let relevant_dynamic var = + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo var) @@ fun () -> if LibraryFunctions.is_special var then let desc = LibraryFunctions.find var in (* We don't really have arguments at hand, so we cheat and just feed it a list of MyCFG.unknown_exp of appropriate length *) @@ -186,7 +189,7 @@ let enableAnalyses anas = (*escape is also still enabled, because otherwise we get a warning*) (*does not consider dynamic calls!*) -let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"; "mhp"; "region"] +let notNeccessaryThreadAnalyses = ["race"; "deadlock"; "maylocks"; "symb_locks"; "thread"; "threadid"; "threadJoins"; "threadreturn"; "mhp"; "region"; "pthreadMutexType"] let reduceThreadAnalyses () = let isThreadCreate = function | LibraryDesc.ThreadCreate _ -> true diff --git a/src/build-info/dune b/src/build-info/dune index 5a64b399a4..1dd74c192c 100644 --- a/src/build-info/dune +++ b/src/build-info/dune @@ -8,14 +8,13 @@ (library (name goblint_build_info) (public_name goblint.build-info) - (libraries batteries.unthreaded) (virtual_modules dune_build_info)) (rule (target configVersion.ml) (mode (promote (until-clean) (only configVersion.ml))) ; replace existing file in source tree, even if releasing (only overrides) (deps (universe)) ; do not cache, always regenerate - (action (pipe-stdout (bash "git describe --all --long --dirty || echo \"n/a\"") (with-stdout-to %{target} (bash "xargs printf '(* Automatically regenerated, changes do not persist! *)\nlet version = \"%s\"'"))))) + (action (pipe-stdout (bash "git describe --all --long --dirty || echo \"n/a\"") (with-stdout-to %{target} (bash "xargs printf '(* Automatically regenerated, changes do not persist! *)\nlet version = Sys.opaque_identity \"%s\"'"))))) (rule (target configProfile.ml) @@ -31,7 +30,7 @@ (target configDatetime.ml) (mode (promote (until-clean) (only configDatetime.ml))) ; replace existing file in source tree, even if releasing (only overrides) (deps (universe)) ; do not cache, always regenerate - (action (pipe-stdout (bash "date +\"%Y-%m-%dT%H:%M:%S\" || echo \"n/a\"") (with-stdout-to %{target} (bash "xargs printf '(* Automatically regenerated, changes do not persist! *)\nlet datetime = \"%s\"'"))))) + (action (pipe-stdout (bash "date +\"%Y-%m-%dT%H:%M:%S\" || echo \"n/a\"") (with-stdout-to %{target} (bash "xargs printf '(* Automatically regenerated, changes do not persist! *)\nlet datetime = Sys.opaque_identity \"%s\"'"))))) (env (_ diff --git a/src/build-info/goblint_build_info.ml b/src/build-info/goblint_build_info.ml index 14e30a1b36..d2600c94ad 100644 --- a/src/build-info/goblint_build_info.ml +++ b/src/build-info/goblint_build_info.ml @@ -18,7 +18,7 @@ let release_commit = "%%VCS_COMMIT_ID%%" (** Goblint version. *) let version = let commit = ConfigVersion.version in - if BatString.starts_with release_version "%" then + if String.starts_with release_version ~prefix:"%" then commit else ( let commit = diff --git a/src/cdomain/value/cdomains/arrayDomain.ml b/src/cdomain/value/cdomains/arrayDomain.ml index e1cfb96425..4192489c3a 100644 --- a/src/cdomain/value/cdomains/arrayDomain.ml +++ b/src/cdomain/value/cdomains/arrayDomain.ml @@ -834,7 +834,7 @@ end let array_oob_check ( type a ) (module Idx: IntDomain.Z with type t = a) (x, l) (e, v) = if GobConfig.get_bool "ana.arrayoob" then (* The purpose of the following 2 lines is to give the user extra info about the array oob *) let idx_before_end = Idx.to_bool (Idx.lt v l) (* check whether index is before the end of the array *) - and idx_after_start = Idx.to_bool (Idx.ge v (Idx.of_int Cil.ILong Z.zero)) in (* check whether the index is non-negative *) + and idx_after_start = Idx.to_bool (Idx.ge v (Idx.of_int (Cilfacade.ptrdiff_ikind ()) Z.zero)) in (* check whether the index is non-negative *) (* For an explanation of the warning types check the Pull Request #255 *) match(idx_after_start, idx_before_end) with | Some true, Some true -> (* Certainly in bounds on both sides.*) diff --git a/src/cdomain/value/cdomains/concDomain.ml b/src/cdomain/value/cdomains/concDomain.ml index 5f609a31d8..467159c9da 100644 --- a/src/cdomain/value/cdomains/concDomain.ml +++ b/src/cdomain/value/cdomains/concDomain.ml @@ -1,7 +1,7 @@ (** Domains for thread sets and their uniqueness. *) -module ThreadSet = -struct +module ThreadSet = +struct include SetDomain.Make (ThreadIdDomain.Thread) let is_top = mem UnknownThread @@ -27,10 +27,11 @@ module CreatedThreadSet = ThreadSet module ThreadCreation = struct module UNames = struct - let truename = "repeated" - let falsename = "unique" + let name = "unique" + let true_name = "repeated" + let false_name = "unique" end - module Uniqueness = IntDomain.MakeBooleans (UNames) + module Uniqueness = BoolDomain.MakeMayBool (UNames) module ParentThreadSet = struct include ThreadSet @@ -38,12 +39,13 @@ struct end module DirtyExitNames = struct - let truename = "dirty exit" - let falsename = "clean exit" + let name = "exit" + let true_name = "dirty exit" + let false_name = "clean exit" end (* A thread exits cleanly iff it joined all threads it started, and they also all exit cleanly *) - module DirtyExit = IntDomain.MakeBooleans (DirtyExitNames) + module DirtyExit = BoolDomain.MakeMayBool (DirtyExitNames) include Lattice.Prod3 (Uniqueness) (ParentThreadSet) (DirtyExit) end diff --git a/src/cdomain/value/cdomains/floatDomain.ml b/src/cdomain/value/cdomains/floatDomain.ml index 0977c68890..7c77b954b4 100644 --- a/src/cdomain/value/cdomains/floatDomain.ml +++ b/src/cdomain/value/cdomains/floatDomain.ml @@ -1134,7 +1134,7 @@ module FloatDomTupleImpl = struct module F1 = FloatIntervalImplLifted open Batteries - type t = F1.t option [@@deriving to_yojson, eq, ord] + type t = F1.t option [@@deriving eq, ord, hash] let name () = "floatdomtuple" @@ -1181,10 +1181,6 @@ module FloatDomTupleImpl = struct Option.map_default identity "" (mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) x -> F.name () ^ ":" ^ F.show x); } x) - let hash x = - Option.map_default identity 0 - (mapp { fp= (fun (type a) (module F : FloatDomain with type t = a) -> F.hash); } x) - let of_const fkind = create { fi= (fun (type a) (module F : FloatDomain with type t = a) -> F.of_const fkind); } diff --git a/src/cdomain/value/cdomains/intDomain.ml b/src/cdomain/value/cdomains/intDomain.ml index c525732d3b..e50b3f26cc 100644 --- a/src/cdomain/value/cdomains/intDomain.ml +++ b/src/cdomain/value/cdomains/intDomain.ml @@ -582,6 +582,59 @@ module IntervalArith (Ints_t : IntOps.IntOps) = struct List.exists (Z.equal l) ts end +module IntInvariant = +struct + let of_int e ik x = + if get_bool "witness.invariant.exact" then + Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) + else + Invariant.none + + let of_incl_list e ik ps = + match ps with + | [_; _] when ik = IBool && not (get_bool "witness.invariant.inexact-type-bounds") -> + assert (List.mem Z.zero ps); + assert (List.mem Z.one ps); + Invariant.none + | [_] when get_bool "witness.invariant.exact" -> + Invariant.none + | _ :: _ :: _ + | [_] | [] -> + List.fold_left (fun a x -> + let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in + Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) + ) (Invariant.bot ()) ps + + let of_interval_opt e ik = function + | (Some x1, Some x2) when Z.equal x1 x2 -> + of_int e ik x1 + | x1_opt, x2_opt -> + let (min_ik, max_ik) = Size.range ik in + let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in + let i1 = + match x1_opt, inexact_type_bounds with + | Some x1, false when Z.equal min_ik x1 -> Invariant.none + | Some x1, _ -> Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1, e, intType)) + | None, _ -> Invariant.none + in + let i2 = + match x2_opt, inexact_type_bounds with + | Some x2, false when Z.equal x2 max_ik -> Invariant.none + | Some x2, _ -> Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2, intType)) + | None, _ -> Invariant.none + in + Invariant.(i1 && i2) + + let of_interval e ik (x1, x2) = + of_interval_opt e ik (Some x1, Some x2) + + let of_excl_list e ik ns = + List.fold_left (fun a x -> + let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in + Invariant.(a && i) + ) (Invariant.top ()) ns +end + module IntervalFunctor (Ints_t : IntOps.IntOps): SOverflow with type int_t = Ints_t.t and type t = (Ints_t.t * Ints_t.t) option = struct let name () = "intervals" @@ -915,21 +968,10 @@ struct else if Ints_t.compare y2 x1 <= 0 then of_bool ik false else top_bool - let invariant_ikind e ik x = - match x with - | Some (x1, x2) when Ints_t.compare x1 x2 = 0 -> - if get_bool "witness.invariant.exact" then - let x1 = Ints_t.to_bigint x1 in - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x1, intType)) - else - Invariant.top () + let invariant_ikind e ik = function | Some (x1, x2) -> - let (min_ik, max_ik) = range ik in - let (x1', x2') = BatTuple.Tuple2.mapn (Ints_t.to_bigint) (x1, x2) in - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in - let i1 = if inexact_type_bounds || Ints_t.compare min_ik x1 <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik x1', e, intType)) else Invariant.none in - let i2 = if inexact_type_bounds || Ints_t.compare x2 max_ik <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik x2', intType)) else Invariant.none in - Invariant.(i1 && i2) + let (x1', x2') = BatTuple.Tuple2.mapn Ints_t.to_bigint (x1, x2) in + IntInvariant.of_interval e ik (x1', x2') | None -> Invariant.none let arbitrary ik = @@ -2297,25 +2339,14 @@ struct let invariant_ikind e ik (x:t) = match x with | `Definite x -> - if get_bool "witness.invariant.exact" then - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) - else - Invariant.top () + IntInvariant.of_int e ik x | `Excluded (s, r) -> (* Emit range invariant if tighter than ikind bounds. This can be more precise than interval, which has been widened. *) let (rmin, rmax) = (Exclusion.min_of_range r, Exclusion.max_of_range r) in - let (ikmin, ikmax) = - let ikr = size ik in - (Exclusion.min_of_range ikr, Exclusion.max_of_range ikr) - in - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in - let imin = if inexact_type_bounds || Z.compare ikmin rmin <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik rmin, e, intType)) else Invariant.none in - let imax = if inexact_type_bounds || Z.compare rmax ikmax <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik rmax, intType)) else Invariant.none in - S.fold (fun x a -> - let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in - Invariant.(a && i) - ) s Invariant.(imin && imax) + let ri = IntInvariant.of_interval e ik (rmin, rmax) in + let si = IntInvariant.of_excl_list e ik (S.elements s) in + Invariant.(ri && si) | `Bot -> Invariant.none let arbitrary ik = @@ -2345,72 +2376,6 @@ struct let project ik p t = t end -(* BOOLEAN DOMAINS *) - -module type BooleansNames = -sig - val truename: string - val falsename: string -end - -module MakeBooleans (N: BooleansNames) = -struct - type int_t = IntOps.Int64Ops.t - type t = bool [@@deriving eq, ord, hash, to_yojson] - let name () = "booleans" - let top () = true - let bot () = false - let top_of ik = top () - let bot_of ik = bot () - let show x = if x then N.truename else N.falsename - include Std (struct type nonrec t = t let name = name let top_of = top_of let bot_of = bot_of let show = show let equal = equal end) - let is_top x = x (* override Std *) - - let equal_to i x = if x then `Top else failwith "unsupported: equal_to with bottom" - let cast_to ?(suppress_ovwarn=false) ?torg _ x = x (* ok since there's no smaller ikind to cast to *) - - let leq x y = not x || y - let join = (||) - let widen = join - let meet = (&&) - let narrow = meet - - let of_bool x = x - let to_bool x = Some x - let of_int x = x = Int64.zero - let to_int x = if x then None else Some Int64.zero - - let neg x = x - let add x y = x || y - let sub x y = x || y - let mul x y = x && y - let div x y = true - let rem x y = true - let lt n1 n2 = true - let gt n1 n2 = true - let le n1 n2 = true - let ge n1 n2 = true - let eq n1 n2 = true - let ne n1 n2 = true - let lognot x = true - let logand x y = x && y - let logor x y = x || y - let logxor x y = x && not y || not x && y - let shift_left n1 n2 = n1 - let shift_right n1 n2 = n1 - let c_lognot = (not) - let c_logand = (&&) - let c_logor = (||) - let arbitrary () = QCheck.bool - let invariant _ _ = Invariant.none (* TODO *) -end - -module Booleans = MakeBooleans ( - struct - let truename = "True" - let falsename = "False" - end) - (* Inclusion/Exclusion sets. Go to top on arithmetic operations (except for some easy cases, e.g. multiplication with 0). Joins on widen, i.e. precise integers as long as not derived from arithmetic expressions. *) module Enums : S with type int_t = Z.t = struct module R = Interval32 (* range for exclusion *) @@ -2731,32 +2696,16 @@ module Enums : S with type int_t = Z.t = struct let ne ik x y = c_lognot ik (eq ik x y) let invariant_ikind e ik x = - let inexact_type_bounds = get_bool "witness.invariant.inexact-type-bounds" in match x with - | Inc ps when not inexact_type_bounds && ik = IBool && is_top_of ik x -> - Invariant.none | Inc ps -> - if BISet.cardinal ps > 1 || get_bool "witness.invariant.exact" then - BISet.fold (fun x a -> - let i = Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik x, intType)) in - Invariant.(a || i) [@coverage off] (* bisect_ppx cannot handle redefined (||) *) - ) ps (Invariant.bot ()) - else - Invariant.top () + IntInvariant.of_incl_list e ik (BISet.elements ps) | Exc (ns, r) -> (* Emit range invariant if tighter than ikind bounds. This can be more precise than interval, which has been widened. *) let (rmin, rmax) = (Exclusion.min_of_range r, Exclusion.max_of_range r) in - let (ikmin, ikmax) = - let ikr = size ik in - (Exclusion.min_of_range ikr, Exclusion.max_of_range ikr) - in - let imin = if inexact_type_bounds || Z.compare ikmin rmin <> 0 then Invariant.of_exp Cil.(BinOp (Le, kintegerCilint ik rmin, e, intType)) else Invariant.none in - let imax = if inexact_type_bounds || Z.compare rmax ikmax <> 0 then Invariant.of_exp Cil.(BinOp (Le, e, kintegerCilint ik rmax, intType)) else Invariant.none in - BISet.fold (fun x a -> - let i = Invariant.of_exp Cil.(BinOp (Ne, e, kintegerCilint ik x, intType)) in - Invariant.(a && i) - ) ns Invariant.(imin && imax) + let ri = IntInvariant.of_interval e ik (rmin, rmax) in + let nsi = IntInvariant.of_excl_list e ik (BISet.elements ns) in + Invariant.(ri && nsi) let arbitrary ik = @@ -2779,7 +2728,7 @@ module Enums : S with type int_t = Z.t = struct | Inc e, Some (c, m) -> Inc (BISet.filter (contains c m) e) | _ -> a - let refine_with_interval ik a b = a + let refine_with_interval ik a b = a (* TODO: refine inclusion (exclusion?) set *) let refine_with_excl_list ik a b = match b with @@ -3243,10 +3192,7 @@ struct match x with | x when is_top x -> Invariant.top () | Some (c, m) when m =: Z.zero -> - if get_bool "witness.invariant.exact" then - Invariant.of_exp Cil.(BinOp (Eq, e, Cil.kintegerCilint ik c, intType)) - else - Invariant.top () + IntInvariant.of_int e ik c | Some (c, m) -> let open Cil in let (c, m) = BatTuple.Tuple2.mapn (fun a -> kintegerCilint ik a) (c, m) in @@ -3338,7 +3284,7 @@ module IntDomTupleImpl = struct module I5 = IntervalSetFunctor (IntOps.BigIntOps) type t = I1.t option * I2.t option * I3.t option * I4.t option * I5.t option - [@@deriving to_yojson, eq, ord] + [@@deriving eq, ord, hash] let name () = "intdomtuple" @@ -3524,7 +3470,7 @@ module IntDomTupleImpl = struct let merge ps = let (vs, rs) = List.split ps in let (mins, maxs) = List.split rs in - (List.concat vs, (List.min mins, List.max maxs)) + (List.concat vs |> List.sort_uniq Z.compare, (List.min mins, List.max maxs)) in mapp2 { fp2 = fun (type a) (module I:SOverflow with type t = a and type int_t = int_t) -> I.to_excl_list } x |> flat merge @@ -3565,7 +3511,7 @@ module IntDomTupleImpl = struct in [(fun (a, b, c, d, e) -> refine_with_excl_list ik (a, b, c, d, e) (to_excl_list (a, b, c, d, e))); (fun (a, b, c, d, e) -> refine_with_incl_list ik (a, b, c, d, e) (to_incl_list (a, b, c, d, e))); - (fun (a, b, c, d, e) -> maybe refine_with_interval ik (a, b, c, d, e) b); + (fun (a, b, c, d, e) -> maybe refine_with_interval ik (a, b, c, d, e) b); (* TODO: get interval across all domains with minimal and maximal *) (fun (a, b, c, d, e) -> maybe refine_with_congruence ik (a, b, c, d, e) d)] let refine ik ((a, b, c, d, e) : t ) : t = @@ -3665,7 +3611,6 @@ module IntDomTupleImpl = struct |> to_list |> String.concat "; " let to_yojson = [%to_yojson: Yojson.Safe.t list] % to_list % mapp { fp = fun (type a) (module I:SOverflow with type t = a) x -> I.to_yojson x } - let hash = List.fold_left (lxor) 0 % to_list % mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.hash } (* `map/opt_map` are used by `project` *) let opt_map b f = @@ -3778,19 +3723,47 @@ module IntDomTupleImpl = struct | Some v when not (GobConfig.get_bool "dbg.full-output") -> BatPrintf.fprintf f "\n\n%s\n\n\n" (Z.to_string v) | _ -> BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) - let invariant_ikind e ik x = - match to_int x with - | Some v -> - if get_bool "witness.invariant.exact" then - (* If definite, output single equality instead of every subdomain repeating same equality *) - Invariant.of_exp Cil.(BinOp (Eq, e, kintegerCilint ik v, intType)) - else - Invariant.top () - | None -> - let is = to_list (mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.invariant_ikind e ik } x) - in List.fold_left (fun a i -> + let invariant_ikind e ik ((_, _, _, x_cong, x_intset) as x) = + (* TODO: do refinement before to ensure incl_list being more precise than intervals, etc (https://github.com/goblint/analyzer/pull/1517#discussion_r1693998515), requires refine functions to actually refine that *) + let simplify_int fallback = + match to_int x with + | Some v -> + (* If definite, output single equality instead of every subdomain repeating same equality (or something less precise). *) + IntInvariant.of_int e ik v + | None -> + fallback () + in + let simplify_all () = + match to_incl_list x with + | Some ps -> + (* If inclusion set, output disjunction of equalities because it subsumes interval(s), exclusion set and congruence. *) + IntInvariant.of_incl_list e ik ps + | None -> + (* Get interval bounds from all domains (intervals and exclusion set ranges). *) + let min = minimal x in + let max = maximal x in + let ns = Option.map fst (to_excl_list x) |? [] in (* Ignore exclusion set bit range, known via interval bounds already. *) + (* "Refine" out-of-bounds exclusions for simpler output. *) + let ns = Option.map_default (fun min -> List.filter (Z.leq min) ns) ns min in + let ns = Option.map_default (fun max -> List.filter (Z.geq max) ns) ns max in + Invariant.( + IntInvariant.of_interval_opt e ik (min, max) && (* Output best interval bounds once instead of multiple subdomains repeating them (or less precise ones). *) + IntInvariant.of_excl_list e ik ns && + Option.map_default (I4.invariant_ikind e ik) Invariant.none x_cong && (* Output congruence as is. *) + Option.map_default (I5.invariant_ikind e ik) Invariant.none x_intset (* Output interval sets as is. *) + ) + in + let simplify_none () = + let is = to_list (mapp { fp = fun (type a) (module I:SOverflow with type t = a) -> I.invariant_ikind e ik } x) in + List.fold_left (fun a i -> Invariant.(a && i) ) (Invariant.top ()) is + in + match GobConfig.get_string "ana.base.invariant.int.simplify" with + | "none" -> simplify_none () + | "int" -> simplify_int simplify_none + | "all" -> simplify_int simplify_all + | _ -> assert false let arbitrary ik = QCheck.(set_print show @@ tup5 (option (I1.arbitrary ik)) (option (I2.arbitrary ik)) (option (I3.arbitrary ik)) (option (I4.arbitrary ik)) (option (I5.arbitrary ik))) diff --git a/src/cdomain/value/cdomains/intDomain.mli b/src/cdomain/value/cdomains/intDomain.mli index ca64692290..e7667c9b14 100644 --- a/src/cdomain/value/cdomains/intDomain.mli +++ b/src/cdomain/value/cdomains/intDomain.mli @@ -436,27 +436,3 @@ module Reverse (Base: IkindUnawareS): IkindUnawareS with type t = Base.t and typ (* module IncExcInterval : S with type t = [ | `Excluded of Interval.t| `Included of Interval.t ] *) (** Inclusive and exclusive intervals. Warning: NOT A LATTICE *) module Enums : S with type int_t = Z.t - -(** {b Boolean domains} *) - -module type BooleansNames = -sig - val truename: string - (** The name of the [true] abstract value *) - - val falsename: string - (** The name of the [false] abstract value *) -end -(** Parameter signature for the [MakeBooleans] functor. *) - -module MakeBooleans (Names: BooleansNames): IkindUnawareS with type t = bool -(** Creates an abstract domain for integers represented by boolean values. *) - -module Booleans: IkindUnawareS with type t = bool -(** Boolean abstract domain, where true is output "True" and false is output - * "False" *) - -(* -module None: S with type t = unit -(** Domain with nothing in it. *) -*) diff --git a/src/cdomain/value/cdomains/mutexAttrDomain.ml b/src/cdomain/value/cdomains/mutexAttrDomain.ml index ea9696d26f..e12818c2c1 100644 --- a/src/cdomain/value/cdomains/mutexAttrDomain.ml +++ b/src/cdomain/value/cdomains/mutexAttrDomain.ml @@ -25,7 +25,7 @@ let recursive_int = lazy ( let res = ref (Z.of_int 2) in (* Use OS X as the default, it doesn't have the enum *) GoblintCil.iterGlobals !Cilfacade.current_file (function | GEnumTag (einfo, _) -> - List.iter (fun (name, exp, _) -> + List.iter (fun (name, _, exp, _) -> if name = "PTHREAD_MUTEX_RECURSIVE" then res := Option.get @@ GoblintCil.getInteger exp ) einfo.eitems diff --git a/src/cdomain/value/cdomains/offset.ml b/src/cdomain/value/cdomains/offset.ml index 9c9c5c3333..e8cba0afc5 100644 --- a/src/cdomain/value/cdomains/offset.ml +++ b/src/cdomain/value/cdomains/offset.ml @@ -213,7 +213,7 @@ struct let bits_offset, _size = GoblintCil.bitsOffset (TComp (field.fcomp, [])) field_as_offset in let bits_offset = idx_of_int bits_offset in let remaining_offset = offset_to_index_offset ~typ:field.ftype o in - Idx.add bits_offset remaining_offset + GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> Idx.add bits_offset remaining_offset | `Index (x, o) -> let (item_typ, item_size_in_bits) = match Option.map unrollType typ with @@ -223,9 +223,10 @@ struct | _ -> (None, Idx.top ()) in - let bits_offset = Idx.mul item_size_in_bits x in + (* Binary operations on offsets should not generate overflow warnings in SV-COMP *) + let bits_offset = GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> Idx.mul item_size_in_bits x in let remaining_offset = offset_to_index_offset ?typ:item_typ o in - Idx.add bits_offset remaining_offset + GobRef.wrap AnalysisState.executing_speculative_computations true @@ fun () -> Idx.add bits_offset remaining_offset in offset_to_index_offset ?typ offs diff --git a/src/cdomain/value/cdomains/valueDomain.ml b/src/cdomain/value/cdomains/valueDomain.ml index 0fbfb50955..f9f4b06ffb 100644 --- a/src/cdomain/value/cdomains/valueDomain.ml +++ b/src/cdomain/value/cdomains/valueDomain.ml @@ -69,7 +69,7 @@ end (* ZeroInit is false if malloc was used to allocate memory and true if calloc was used *) module ZeroInit : ZeroInit = struct - include Lattice.Fake(Basetype.RawBools) + include Lattice.Fake (BoolDomain.Bool) let name () = "zeroinit" let is_malloc x = not x diff --git a/src/cdomain/value/domains/invariantCil.ml b/src/cdomain/value/domains/invariantCil.ml index 813ec25818..f41d48ab61 100644 --- a/src/cdomain/value/domains/invariantCil.ml +++ b/src/cdomain/value/domains/invariantCil.ml @@ -88,7 +88,7 @@ class exp_contains_anon_type_visitor = object inherit nopCilVisitor method! vtype (t: typ) = match t with - | TComp ({cname; _}, _) when BatString.starts_with_stdlib ~prefix:"__anon" cname -> + | TComp ({cname; _}, _) when String.starts_with ~prefix:"__anon" cname -> raise Stdlib.Exit | _ -> DoChildren @@ -102,7 +102,7 @@ let exp_contains_anon_type = (* TODO: synchronize magic constant with BaseDomain *) -let var_is_heap {vname; _} = BatString.starts_with vname "(alloc@" +let var_is_heap {vname; _} = String.starts_with vname ~prefix:"(alloc@" let reset_lazy () = ResettableLazy.reset exclude_vars_regexp diff --git a/src/cdomain/value/util/wideningThresholds.ml b/src/cdomain/value/util/wideningThresholds.ml index 0d93be76ff..939ed9482f 100644 --- a/src/cdomain/value/util/wideningThresholds.ml +++ b/src/cdomain/value/util/wideningThresholds.ml @@ -121,6 +121,7 @@ class extractInvariantsVisitor (exps) = object method! vinst (i: instr) = match i with | Call (_, Lval (Var f, NoOffset), args, _, _) when LibraryFunctions.is_special f -> + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo f) @@ fun () -> let desc = LibraryFunctions.find f in begin match desc.special args with | Assert { exp; _ } -> diff --git a/src/cdomains/apron/affineEqualityDomain.apron.ml b/src/cdomains/apron/affineEqualityDomain.apron.ml index 7e60cce74b..06268130b2 100644 --- a/src/cdomains/apron/affineEqualityDomain.apron.ml +++ b/src/cdomains/apron/affineEqualityDomain.apron.ml @@ -6,7 +6,6 @@ Matrices are modeled as proposed by Karr: Each variable is assigned to a column and each row represents a linear affine relationship that must hold at the corresponding program point. The apron environment is hereby used to organize the order of columns and variables. *) -open Batteries open GoblintCil open Pretty module M = Messages @@ -181,7 +180,7 @@ struct else if Z.lt coeff Z.minus_one then Z.to_string coeff else Format.asprintf "+%s" (Z.to_string coeff) in - coeff_str ^ Var.to_string var + coeff_str ^ Var.show var in let const_to_str vl = if Z.equal vl Z.zero then @@ -193,7 +192,7 @@ struct in let res = (String.concat "" @@ Array.to_list @@ Array.map dim_to_str vars) ^ (const_to_str arr.(Array.length arr - 1)) ^ "=0" in - if String.starts_with res "+" then + if String.starts_with res ~prefix:"+" then Str.string_after res 1 else res @@ -203,11 +202,10 @@ struct | Some m when Matrix.is_empty m -> "⊤" | Some m -> let constraint_list = List.init (Matrix.num_rows m) (fun i -> vec_to_constraint (conv_to_ints @@ Matrix.get_row m i) t.env) in - Format.asprintf "%s" ("[|"^ (String.concat "; " constraint_list) ^"|]") + "[|"^ (String.concat "; " constraint_list) ^"|]" let pretty () (x:t) = text (show x) - let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) - + let printXml f x = BatPrintf.fprintf f "\n\n\nmatrix\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (show x)) Environment.printXml x.env let eval_interval ask = Bounds.bound_texpr let name () = "affeq" @@ -257,7 +255,7 @@ struct let meet t1 t2 = timing_wrap "meet" (meet t1) t2 let leq t1 t2 = - let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) + let env_comp = Environment.cmp t1.env t2.env in (* Apron's Environment.cmp has defined return values. *) if env_comp = -2 || env_comp > 0 then (* -2: environments are not compatible (a variable has different types in the 2 environements *) (* -1: if env1 is a subset of env2, (OK) *) @@ -334,7 +332,7 @@ struct else match Option.get a.d, Option.get b.d with | x, y when is_top_env a || is_top_env b -> {d = Some (Matrix.empty ()); env = Environment.lce a.env b.env} - | x, y when (Environment.compare a.env b.env <> 0) -> + | x, y when (Environment.cmp a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in let mod_y = dim_add (Environment.dimchange b.env sup_env) y in @@ -370,7 +368,7 @@ struct let remove_rels_with_var x var env inplace = timing_wrap "remove_rels_with_var" (remove_rels_with_var x var env) inplace let forget_vars t vars = - if is_bot t || is_top_env t || List.is_empty vars then + if is_bot t || is_top_env t || vars = [] then t else let m = Option.get t.d in @@ -430,8 +428,8 @@ struct let assign_exp ask t var exp no_ov = let res = assign_exp ask t var exp no_ov in - if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s" - (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; + if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %a \n exp: %a\n no_ov: %b -> \n %s" + (show t) Var.pretty var d_exp exp (Lazy.force no_ov) (show res); res let assign_var (t: VarManagement(Vc)(Mx).t) v v' = @@ -441,7 +439,7 @@ struct let assign_var t v v' = let res = assign_var t v v' in - if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s" (show t) (Var.to_string v) (Var.to_string v') (show res) ; + if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %a \n v': %a\n -> %s" (show t) Var.pretty v Var.pretty v' (show res); res let assign_var_parallel t vv's = @@ -499,7 +497,7 @@ struct let substitute_exp ask t var exp no_ov = let res = substitute_exp ask t var exp no_ov in - if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s" (show t) (Var.to_string var) d_exp exp (show res); + if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %a \n exp: %a \n -> \n %s" (show t) Var.pretty var d_exp exp (show res); res let substitute_exp ask t var exp no_ov = timing_wrap "substitution" (substitute_exp ask t var exp) no_ov diff --git a/src/cdomains/apron/apronDomain.apron.ml b/src/cdomains/apron/apronDomain.apron.ml index d0ef268ca6..03ac3ed3f0 100644 --- a/src/cdomains/apron/apronDomain.apron.ml +++ b/src/cdomains/apron/apronDomain.apron.ml @@ -283,7 +283,7 @@ struct let assign_exp_with ask nd v e no_ov = match Convert.texpr1_of_cil_exp ask nd (A.env nd) e no_ov with | texpr1 -> - if M.tracing then M.trace "apron" "assign_exp converted: %s" (Format.asprintf "%a" Texpr1.print texpr1); + if M.tracing then M.trace "apron" "assign_exp converted: %a" Texpr1.pretty texpr1; A.assign_texpr_with Man.mgr nd v texpr1 None | exception Convert.Unsupported_CilExp _ -> if M.tracing then M.trace "apron" "assign_exp unsupported"; @@ -442,7 +442,7 @@ struct let invariant _ = [] let show (x:t) = - Format.asprintf "%a (env: %a)" A.print x (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x) + GobFormat.asprintf "%a (env: %a)" A.print x Environment.pp (A.env x) let pretty () (x:t) = text (show x) let equal x y = @@ -451,10 +451,10 @@ struct let hash (x:t) = A.hash Man.mgr x - let compare (x:t) y: int = - (* there is no A.compare, but polymorphic compare should delegate to Abstract0 and Environment compare's implemented in Apron's C *) - Stdlib.compare x y - let printXml f x = BatPrintf.fprintf f "\n\n\nconstraints\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%a" A.print x)) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x))) + let compare (x: t) (y: t): int = + failwith "Apron.Abstract1 doesn't have total order" (* https://github.com/antoinemine/apron/issues/99 *) + + let printXml f x = BatPrintf.fprintf f "\n\n\nconstraints\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (GobFormat.asprint A.print x)) Environment.printXml (A.env x) let to_yojson (x: t) = let constraints = @@ -463,11 +463,9 @@ struct |> Lincons1Set.elements |> List.map (fun lincons1 -> `String (Lincons1.show lincons1)) in - let env = `String (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (A.env x)) - in `Assoc [ ("constraints", `List constraints); - ("env", env); + ("env", Environment.to_yojson (A.env x)); ] let unify x y = @@ -533,9 +531,9 @@ struct | _ -> begin match Convert.tcons1_of_cil_exp ask d (A.env d) e negate no_ov with | tcons1 -> - if M.tracing then M.trace "apron" "assert_constraint %a %s" d_exp e (Format.asprintf "%a" Tcons1.print tcons1); + if M.tracing then M.trace "apron" "assert_constraint %a %a" d_exp e Tcons1.pretty tcons1; if M.tracing then M.trace "apron" "assert_constraint st: %a" D.pretty d; - if M.tracing then M.trace "apron" "assert_constraint tcons1: %s" (Format.asprintf "%a" Tcons1.print tcons1); + if M.tracing then M.trace "apron" "assert_constraint tcons1: %a" Tcons1.pretty tcons1; let r = meet_tcons ask d tcons1 e in if M.tracing then M.trace "apron" "assert_constraint r: %a" D.pretty r; r @@ -598,7 +596,7 @@ struct let x_cons = A.to_lincons_array Man.mgr x_j in let y_cons = A.to_lincons_array Man.mgr y_j in let try_add_con j con1 = - if M.tracing then M.tracei "apron" "try_add_con %s" (Format.asprintf "%a" (Lincons1.print: Format.formatter -> Lincons1.t -> unit) con1); + if M.tracing then M.tracei "apron" "try_add_con %a" Lincons1.pretty con1; let t = meet_lincons j con1 in let t_x = A.change_environment Man.mgr t x_env false in let t_y = A.change_environment Man.mgr t y_env false in @@ -637,7 +635,7 @@ struct in let env_exists_mem_con1 env con1 = let r = env_exists_mem_con1 env con1 in - if M.tracing then M.trace "apron" "env_exists_mem_con1 %s %s -> %B" (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) env) (Lincons1.show con1) r; + if M.tracing then M.trace "apron" "env_exists_mem_con1 %a %a -> %B" Environment.pretty env Lincons1.pretty con1 r; r in (* Heuristically reorder constraints to pass 36/12 with singlethreaded->multithreaded mode switching. *) diff --git a/src/cdomains/apron/gobApron.apron.ml b/src/cdomains/apron/gobApron.apron.ml index e202a88c60..fbb1fe9ec5 100644 --- a/src/cdomains/apron/gobApron.apron.ml +++ b/src/cdomains/apron/gobApron.apron.ml @@ -1,6 +1,19 @@ open Batteries include Apron +module Scalar = +struct + include Scalar + + let pp = print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) +end + module Coeff = struct include Coeff @@ -11,6 +24,15 @@ end module Var = struct include Var + + let pp = print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) + let equal x y = Var.compare x y = 0 end @@ -18,8 +40,17 @@ module Lincons1 = struct include Lincons1 - let show = Format.asprintf "%a" print - let compare x y = String.compare (show x) (show y) (* HACK *) + let pp = print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) + + let compare x y = + (* TODO: implement proper total Lincons1 order *) + String.compare (show x) (show y) (* HACK *) let num_vars x = (* Apron.Linexpr0.get_size returns some internal nonsense, so we count ourselves. *) @@ -43,12 +74,63 @@ struct |> of_enum end +module Texpr1 = +struct + include Texpr1 + + let pp = print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) + + module Expr = + struct + type t = expr + + let pp = print_expr + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) + end +end + +module Tcons1 = +struct + include Tcons1 + + let pp = print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) +end + (** A few code elements for environment changes from functions as remove_vars etc. have been moved to sharedFunctions as they are needed in a similar way inside affineEqualityDomain. A module that includes various methods used by variable handling operations such as add_vars, remove_vars etc. in apronDomain and affineEqualityDomain. *) module Environment = struct include Environment + let pp: Format.formatter -> Environment.t -> unit = Environment.print + include Printable.SimpleFormat ( + struct + type nonrec t = t + let pp = pp + end + ) + + let compare (x: t) (y: t): int = + (* TODO: implement total Environment order in OCaml *) + failwith "Apron.Environment doesn't have total order" (* https://github.com/antoinemine/apron/issues/99 *) + let ivars_only env = let ivs, fvs = Environment.vars env in assert (Array.length fvs = 0); (* shouldn't ever contain floats *) diff --git a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml index 5558bc2c96..c1ca3661a5 100644 --- a/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml +++ b/src/cdomains/apron/linearTwoVarEqualityDomain.apron.ml @@ -11,7 +11,7 @@ open Batteries open GoblintCil open Pretty module M = Messages -open Apron +open GobApron open VectorMatrix module Mpqf = SharedFunctions.Mpqf @@ -158,7 +158,7 @@ module EqualitiesConjunction = struct let (newref,offs,divi) = (get_rhs d head) in let (coeff,y) = BatOption.get newref in let (y,yrhs) = inverse head (coeff,y,offs,divi) in (* reassemble yrhs out of components *) - let shifted_cluster = (List.fold (fun map i -> + let shifted_cluster = (List.fold (fun map i -> let irhs = (get_rhs d i) in (* old entry is i = irhs *) Rhs.subst yrhs y irhs |> (* new entry for i is irhs [yrhs/y] *) set_rhs map i @@ -222,7 +222,7 @@ module EqualitiesConjunction = struct | Some (coeff,j), ((Some (coeff1,h1), o1, divi1) as oldi)-> (match get_rhs ts j with (* ts[x_j]=o2/d2 ========> ... *) - | (None , o2, divi2) -> + | (None , o2, divi2) -> let newxi = Rhs.subst (None,o2,divi2) j (Some (coeff,j),offs,divi) in let newxh1 = snd @@ inverse i (coeff1,h1,o1,divi1) in let newxh1 = Rhs.subst newxi i newxh1 in @@ -251,7 +251,7 @@ module EqualitiesConjunction = struct else (* var_i = var_i, i.e. it may occur on the rhs of other equalities *) (* so now, we transform with the inverse of the transformer: *) let inv = snd (inverse i (coeff,j,offs,divi)) in - IntMap.fold (fun k v acc -> + IntMap.fold (fun k v acc -> match v with | (Some (c,x),o,d) when x=i-> set_rhs acc k (Rhs.subst inv i v) | _ -> acc @@ -281,7 +281,7 @@ struct let multiply a b = (* if one of them is a constant, then multiply. Otherwise, the expression is not linear *) match a, b with - | [(None,coeff, divi)], c + | [(None,coeff, divi)], c | c, [(None,coeff, divi)] -> multiply_with_Q coeff divi c | _ -> raise NotLinearExpr in @@ -314,7 +314,7 @@ struct | x -> Some(x) (** convert and simplify (wrt. reference variables) a texpr into a tuple of a list of monomials (coeff,varidx,divi) and a (constant/divi) *) - let simplified_monomials_from_texp (t: t) texp = + let simplified_monomials_from_texp (t: t) texp = BatOption.bind (monomials_from_texp t texp) (fun monomiallist -> let d = Option.get t.d in @@ -323,7 +323,7 @@ struct | None -> let gcdee = Z.gcd adiv divi in exprcache,(Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi) | Some (coeff,idx) -> let (somevar,someoffs,somedivi)=Rhs.subst (EConj.get_rhs d idx) idx (v,offs,divi) in (* normalize! *) let newcache = Option.map_default (fun (coef,ter) -> IMap.add ter Q.((IMap.find_default zero ter exprcache) + make coef somedivi) exprcache) exprcache somevar in - let gcdee = Z.gcd adiv divi in + let gcdee = Z.gcd adiv divi in (newcache,(Z.(aconst*divi/gcdee + offs*adiv/gcdee),Z.lcm adiv divi)) in let (expr,constant) = List.fold_left accumulate_constants (IMap.empty,(Z.zero,Z.one)) monomiallist in (* abstract simplification of the guard wrt. reference variables *) @@ -331,7 +331,7 @@ struct let simplified_monomials_from_texp (t: t) texp = let res = simplified_monomials_from_texp t texp in - if M.tracing then M.tracel "from_texp" "%s %s -> %s" (EConj.show @@ snd @@ BatOption.get t.d) (Format.asprintf "%a" Texpr1.print_expr texp) + if M.tracing then M.tracel "from_texp" "%s %a -> %s" (EConj.show @@ snd @@ BatOption.get t.d) Texpr1.Expr.pretty texp (BatOption.map_default (fun (l,(o,d)) -> List.fold_right (fun (a,x,b) acc -> Printf.sprintf "%s*var_%d/%s + %s" (Z.to_string a) x (Z.to_string b) acc) l ((Z.to_string o)^"/"^(Z.to_string d))) "" res); res @@ -339,7 +339,7 @@ struct BatOption.bind (simplified_monomials_from_texp t texp ) (fun (sum_of_terms, (constant,divisor)) -> (match sum_of_terms with - | [] -> Some (None, constant,divisor) + | [] -> Some (None, constant,divisor) | [(coeff,var,divi)] -> Some (Rhs.canonicalize (Some (Z.mul divisor coeff,var), Z.mul constant divi,Z.mul divisor divi)) |_ -> None)) @@ -361,7 +361,7 @@ struct else match simplify_to_ref_and_offset t (Texpr1.to_expr texpr) with | Some (None, offset, divisor) when Z.equal (Z.rem offset divisor) Z.zero -> let res = Z.div offset divisor in - (if M.tracing then M.tracel "bounds" "min: %s max: %s" (IntOps.BigIntOps.to_string res) (IntOps.BigIntOps.to_string res); + (if M.tracing then M.tracel "bounds" "min: %a max: %a" GobZ.pretty res GobZ.pretty res; Some res, Some res) | _ -> None, None @@ -424,7 +424,7 @@ struct EConj.show_formatted (show_var varM.env) (snd arr) ^ (to_subscript @@ fst arr) let pretty () (x:t) = text (show x) - let printXml f x = BatPrintf.fprintf f "\n\n\nequalities\n\n\n%s\n\nenv\n\n\n%s\n\n\n" (XmlUtil.escape (Format.asprintf "%s" (show x) )) (XmlUtil.escape (Format.asprintf "%a" (Environment.print: Format.formatter -> Environment.t -> unit) (x.env))) + let printXml f x = BatPrintf.fprintf f "\n\n\nequalities\n\n\n%s\n\nenv\n\n\n%a\n\n\n" (XmlUtil.escape (show x)) Environment.printXml x.env let eval_interval ask = Bounds.bound_texpr let meet_with_one_conj t i (var, o, divi) = @@ -447,7 +447,7 @@ struct let t1 = change_d t1 sup_env ~add:true ~del:false in let t2 = change_d t2 sup_env ~add:true ~del:false in match t1.d, t2.d with - | Some d1', Some d2' -> + | Some d1', Some d2' -> EConj.IntMap.fold (fun lhs rhs map -> meet_with_one_conj map lhs rhs) (snd d2') t1 (* even on sparse d2, this will chose the relevant conjs to meet with*) | _ -> {d = None; env = sup_env} @@ -459,7 +459,7 @@ struct let meet t1 t2 = timing_wrap "meet" (meet t1) t2 let leq t1 t2 = - let env_comp = Environment.compare t1.env t2.env in (* Apron's Environment.compare has defined return values. *) + let env_comp = Environment.cmp t1.env t2.env in (* Apron's Environment.cmp has defined return values. *) let implies ts i (var, offs, divi) = let tuple_cmp = Tuple3.eq (Option.eq ~eq:(Tuple2.eq (Z.equal) (Int.equal))) (Z.equal) (Z.equal) in match var with @@ -489,7 +489,7 @@ struct - lhs itself - criteria A and B that characterize equivalence class, depending on the reference variable and the affine expression parameters wrt. each EConj - rhs1 - - rhs2 + - rhs2 however, we have to account for the sparseity of EConj maps by manually patching holes with default values *) let joinfunction lhs rhs1 rhs2 = ( @@ -516,15 +516,15 @@ struct let varentry ci offi ch offh xh = let (coeff,off,d) = Q.(ci,(offi*ch)-(ci*offh),ch) in (* compute new rhs in Q *) let (coeff,off,d) = Z.(coeff.num*d.den*off.den,off.num*d.den*coeff.den,d. num*coeff.den*off.den) in (* convert that back into Z *) - Rhs.canonicalize (Some(coeff,xh),off,d) + Rhs.canonicalize (Some(coeff,xh),off,d) in (* ci1 = a*ch1+b /\ ci2 = a*ch2+b *) (* ===> a = (ci1-ci2)/(ch1-ch2) b = ci2-a*ch2 *) - let constentry ci1 ci2 ch1 ch2 xh = + let constentry ci1 ci2 ch1 ch2 xh = let a = Q.((ci1-ci2) / (ch1-ch2)) in let b = Q.(ci2 - a*ch2) in Rhs.canonicalize (Some (Z.(a.num*b.den),xh),Z.(b.num*a.den) ,Z.(a.den*b.den) ) in - let iterate map l = + let iterate map l = match l with | (_, _, _, rhs , rhs' ) :: t when Rhs.equal rhs rhs' -> List.fold (fun acc (x,_,_,rh,_) -> EConj.set_rhs acc x rh) map l | (h, _, _, ((Some (ch,_),oh,dh)), ((Some _,_,_) )) :: t -> List.fold (fun acc (i,_,_,(monom,oi,di),_) -> EConj.set_rhs acc i (varentry Q.(make (fst@@Option.get monom) di) Q.(make oi di) Q.(make ch dh) Q.(make oh dh) h)) map t @@ -543,7 +543,7 @@ struct | Some x, Some y when is_top a || is_top b -> let new_env = Environment.lce a.env b.env in top_of new_env - | Some x, Some y when (Environment.compare a.env b.env <> 0) -> + | Some x, Some y when (Environment.cmp a.env b.env <> 0) -> let sup_env = Environment.lce a.env b.env in let mod_x = dim_add (Environment.dimchange a.env sup_env) x in let mod_y = dim_add (Environment.dimchange b.env sup_env) y in @@ -630,8 +630,8 @@ struct let assign_exp ask t var exp no_ov = let res = assign_exp ask t var exp no_ov in - if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %s \n exp: %a\n no_ov: %b -> \n %s" - (show t) (Var.to_string var) d_exp exp (Lazy.force no_ov) (show res) ; + if M.tracing then M.tracel "ops" "assign_exp t:\n %s \n var: %a \n exp: %a\n no_ov: %b -> \n %s" + (show t) Var.pretty var d_exp exp (Lazy.force no_ov) (show res); res let assign_var (t: VarManagement.t) v v' = @@ -640,7 +640,7 @@ struct let assign_var t v v' = let res = assign_var t v v' in - if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %s \n v': %s\n -> %s" (show t) (Var.to_string v) (Var.to_string v') (show res) ; + if M.tracing then M.tracel "ops" "assign_var t:\n %s \n v: %a \n v': %a\n -> %s" (show t) Var.pretty v Var.pretty v' (show res); res (** Parallel assignment of variables. @@ -693,7 +693,7 @@ struct let substitute_exp ask t var exp no_ov = let res = substitute_exp ask t var exp no_ov in - if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %s \n exp: %a \n -> \n %s" (show t) (Var.to_string var) d_exp exp (show res); + if M.tracing then M.tracel "ops" "Substitute_expr t: \n %s \n var: %a \n exp: %a \n -> \n %s" (show t) Var.pretty var d_exp exp (show res); res let substitute_exp ask t var exp no_ov = timing_wrap "substitution" (substitute_exp ask t var exp) no_ov diff --git a/src/cdomains/apron/relationDomain.apron.ml b/src/cdomains/apron/relationDomain.apron.ml index 9b1eff0c64..3b7226889f 100644 --- a/src/cdomains/apron/relationDomain.apron.ml +++ b/src/cdomains/apron/relationDomain.apron.ml @@ -146,7 +146,7 @@ end type ('a, 'b) relcomponents_t = { rel: 'a; priv: 'b; -} [@@deriving eq, ord, hash, to_yojson] +} [@@deriving eq, ord, hash, to_yojson, relift, lattice] module RelComponents (D3: S3) (PrivD: Lattice.S): sig @@ -155,13 +155,11 @@ sig end = struct module RD = D3 - type t = (RD.t, PrivD.t) relcomponents_t [@@deriving eq, ord, hash, to_yojson] + type t = (RD.t, PrivD.t) relcomponents_t [@@deriving eq, ord, hash, to_yojson, relift, lattice] include Printable.Std open Pretty - let relift {rel; priv} = {rel = RD.relift rel; priv = PrivD.relift priv} - let show r = let first = RD.show r.rel in let third = PrivD.show r.priv in @@ -185,26 +183,11 @@ struct let tr = QCheck.pair (RD.arbitrary ()) (PrivD.arbitrary ()) in QCheck.map ~rev:to_tuple of_tuple tr - let bot () = {rel = RD.bot (); priv = PrivD.bot ()} - let is_bot {rel; priv} = RD.is_bot rel && PrivD.is_bot priv - let top () = {rel = RD.top (); priv = PrivD.bot ()} - let is_top {rel; priv} = RD.is_top rel && PrivD.is_top priv - - let leq {rel=x1; priv=x3 } {rel=y1; priv=y3} = - RD.leq x1 y1 && PrivD.leq x3 y3 - let pretty_diff () (({rel=x1; priv=x3}:t),({rel=y1; priv=y3}:t)): Pretty.doc = if not (RD.leq x1 y1) then RD.pretty_diff () (x1,y1) else PrivD.pretty_diff () (x3,y3) - - let op_scheme op1 op3 {rel=x1; priv=x3} {rel=y1; priv=y3}: t = - {rel = op1 x1 y1; priv = op3 x3 y3 } - let join = op_scheme RD.join PrivD.join - let meet = op_scheme RD.meet PrivD.meet - let widen = op_scheme RD.widen PrivD.widen - let narrow = op_scheme RD.narrow PrivD.narrow end diff --git a/src/cdomains/apron/sharedFunctions.apron.ml b/src/cdomains/apron/sharedFunctions.apron.ml index 8c94c996b0..86b5f2770f 100644 --- a/src/cdomains/apron/sharedFunctions.apron.ml +++ b/src/cdomains/apron/sharedFunctions.apron.ml @@ -6,7 +6,8 @@ open GobApron module M = Messages -let int_of_scalar ?round (scalar: Scalar.t) = + +let int_of_scalar ?(scalewith=Z.one) ?round (scalar: Scalar.t) = if Scalar.is_infty scalar <> 0 then (* infinity means unbounded *) None else @@ -20,23 +21,26 @@ let int_of_scalar ?round (scalar: Scalar.t) = | None when Stdlib.Float.is_integer f -> Some f | None -> None in - Z.of_float f + Z.(of_float f * scalewith) | Mpqf scalar -> (* octMPQ, boxMPQ, polkaMPQ *) let n = Mpqf.get_num scalar in let d = Mpqf.get_den scalar in + let scale = Z_mlgmpidl.mpz_of_z scalewith in let+ z = if Mpzf.cmp_int d 1 = 0 then (* exact integer (denominator 1) *) - Some n + Some (Mpzf.mul scale n) else begin match round with - | Some `Floor -> Some (Mpzf.fdiv_q n d) (* floor division *) - | Some `Ceil -> Some (Mpzf.cdiv_q n d) (* ceiling division *) - | None -> None + | Some `Floor -> Some (Mpzf.mul scale (Mpzf.fdiv_q n d)) (* floor division *) + | Some `Ceil -> Some (Mpzf.mul scale (Mpzf.cdiv_q n d)) (* ceiling division *) + | None -> let product = Mpzf.mul scale n in if Mpz.divisible_p product d then + Some (Mpzf.divexact product d) (* scale, preferably with common denominator *) + else None end in Z_mlgmpidl.z_of_mpzf z | _ -> - failwith ("int_of_scalar: unsupported: " ^ Scalar.to_string scalar) + failwith ("int_of_scalar: unsupported: " ^ Scalar.show scalar) module type ConvertArg = @@ -136,7 +140,7 @@ struct let expr = (** simplify asks for a constant value of some subexpression e, similar to a constant fold. In particular but not exclusively this query is answered by the 2 var equalities domain itself. This normalizes arbitrary expressions to a point where they - might be able to be represented by means of 2 var equalities + might be able to be represented by means of 2 var equalities This simplification happens during a time, when there are temporary variables a#in and a#out part of the expression, but are not represented in the ctx, thus queries may result in top for these variables. Wrapping this in speculative @@ -196,7 +200,7 @@ struct in let exp = Cil.constFold false exp in let res = conv exp in - if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp: %a -> %s (%b)" d_plainexp exp (Format.asprintf "%a" Texpr1.print_expr res) (Lazy.force no_ov); + if M.tracing then M.trace "relation" "texpr1_expr_of_cil_exp: %a -> %a (%b)" d_plainexp exp Texpr1.Expr.pretty res (Lazy.force no_ov); res let texpr1_of_cil_exp ask d env e no_ov = @@ -245,11 +249,11 @@ module CilOfApron (V: SV) = struct exception Unsupported_Linexpr1 - let cil_exp_of_linexpr1 (linexpr1:Linexpr1.t) = + let cil_exp_of_linexpr1 ?scalewith (linexpr1:Linexpr1.t) = let longlong = TInt(ILongLong,[]) in let coeff_to_const consider_flip (c:Coeff.union_5) = match c with | Scalar c -> - (match int_of_scalar c with + (match int_of_scalar ?scalewith c with | Some i -> let ci,truncation = truncateCilint ILongLong i in if truncation = NoTruncation then @@ -263,15 +267,14 @@ struct else Const (CInt(i,ILongLong,None)), false else - (M.warn ~category:Analyzer "Invariant Apron: coefficient is not int: %s" (Scalar.to_string c); raise Unsupported_Linexpr1) + (M.warn ~category:Analyzer "Invariant Apron: coefficient is not int: %a" Scalar.pretty c; raise Unsupported_Linexpr1) | None -> raise Unsupported_Linexpr1) | _ -> raise Unsupported_Linexpr1 in let expr = ref (fst @@ coeff_to_const false (Linexpr1.get_cst linexpr1)) in let append_summand (c:Coeff.union_5) v = match V.to_cil_varinfo v with - | Some vinfo -> - (* TODO: What to do with variables that have a type that cannot be stored into ILongLong to avoid overflows? *) + | Some vinfo when IntDomain.Size.is_cast_injective ~from_type:vinfo.vtype ~to_type:(TInt(ILongLong,[])) -> let var = Cilfacade.mkCast ~e:(Lval(Var vinfo,NoOffset)) ~newt:longlong in let coeff, flip = coeff_to_const true c in let prod = BinOp(Mult, coeff, var, longlong) in @@ -279,17 +282,45 @@ struct expr := BinOp(MinusA,!expr,prod,longlong) else expr := BinOp(PlusA,!expr,prod,longlong) - | None -> M.warn ~category:Analyzer "Invariant Apron: cannot convert to cil var: %s" (Var.to_string v); raise Unsupported_Linexpr1 + | None -> M.warn ~category:Analyzer "Invariant Apron: cannot convert to cil var: %a" Var.pretty v; raise Unsupported_Linexpr1 + | _ -> M.warn ~category:Analyzer "Invariant Apron: cannot convert to cil var in overflow preserving manner: %a" Var.pretty v; raise Unsupported_Linexpr1 in Linexpr1.iter append_summand linexpr1; !expr + let lcm_den linexpr1 = + let exception UnsupportedScalar + in + let frac_of_scalar scalar = + if Scalar.is_infty scalar <> 0 then (* infinity means unbounded *) + None + else match scalar with + | Float f -> if Stdlib.Float.is_integer f then Some (Q.of_float f) else None + | Mpqf f -> Some (Z_mlgmpidl.q_of_mpqf f) + | _ -> raise UnsupportedScalar + in + let extract_den (c:Coeff.union_5) = + match c with + | Scalar c -> BatOption.map Q.den (frac_of_scalar c) + | _ -> None + in + let lcm_denom = ref (BatOption.default Z.one (extract_den (Linexpr1.get_cst linexpr1))) in + let lcm_coeff (c:Coeff.union_5) _ = + match (extract_den c) with + | Some z -> lcm_denom := Z.lcm z !lcm_denom + | _ -> () + in + try + Linexpr1.iter lcm_coeff linexpr1; !lcm_denom + with UnsupportedScalar -> Z.one + let cil_exp_of_lincons1 (lincons1:Lincons1.t) = let zero = Cil.kinteger ILongLong 0 in try let linexpr1 = Lincons1.get_linexpr1 lincons1 in - let cilexp = cil_exp_of_linexpr1 linexpr1 in + let common_denominator = lcm_den linexpr1 in + let cilexp = cil_exp_of_linexpr1 ~scalewith:common_denominator linexpr1 in match Lincons1.get_typ lincons1 with | EQ -> Some (Cil.constFold false @@ BinOp(Eq, cilexp, zero, TInt(IInt,[]))) | SUPEQ -> Some (Cil.constFold false @@ BinOp(Ge, cilexp, zero, TInt(IInt,[]))) diff --git a/src/cdomains/baseDomain.ml b/src/cdomains/baseDomain.ml index 1be0fb8c1e..64b5a174e8 100644 --- a/src/cdomains/baseDomain.ml +++ b/src/cdomains/baseDomain.ml @@ -38,7 +38,7 @@ type 'a basecomponents_t = { deps: PartDeps.t; weak: WeakUpdates.t; priv: 'a; -} [@@deriving eq, ord, hash] +} [@@deriving eq, ord, hash, relift, lattice] module BaseComponents (PrivD: Lattice.S): @@ -47,7 +47,7 @@ sig val op_scheme: (CPA.t -> CPA.t -> CPA.t) -> (PartDeps.t -> PartDeps.t -> PartDeps.t) -> (WeakUpdates.t -> WeakUpdates.t -> WeakUpdates.t) -> (PrivD.t -> PrivD.t -> PrivD.t) -> t -> t -> t end = struct - type t = PrivD.t basecomponents_t [@@deriving eq, ord, hash] + type t = PrivD.t basecomponents_t [@@deriving eq, ord, hash, relift, lattice] include Printable.Std open Pretty @@ -90,14 +90,6 @@ struct let tr = QCheck.quad (CPA.arbitrary ()) (PartDeps.arbitrary ()) (WeakUpdates.arbitrary ()) (PrivD.arbitrary ()) in QCheck.map ~rev:to_tuple of_tuple tr - let bot () = { cpa = CPA.bot (); deps = PartDeps.bot (); weak = WeakUpdates.bot (); priv = PrivD.bot ()} - let is_bot {cpa; deps; weak; priv} = CPA.is_bot cpa && PartDeps.is_bot deps && WeakUpdates.is_bot weak && PrivD.is_bot priv - let top () = {cpa = CPA.top (); deps = PartDeps.top (); weak = WeakUpdates.top () ; priv = PrivD.top ()} - let is_top {cpa; deps; weak; priv} = CPA.is_top cpa && PartDeps.is_top deps && WeakUpdates.is_top weak && PrivD.is_top priv - - let leq {cpa=x1; deps=x2; weak=x3; priv=x4 } {cpa=y1; deps=y2; weak=y3; priv=y4} = - CPA.leq x1 y1 && PartDeps.leq x2 y2 && WeakUpdates.leq x3 y3 && PrivD.leq x4 y4 - let pretty_diff () (({cpa=x1; deps=x2; weak=x3; priv=x4}:t),({cpa=y1; deps=y2; weak=y3; priv=y4}:t)): Pretty.doc = if not (CPA.leq x1 y1) then CPA.pretty_diff () (x1,y1) @@ -110,13 +102,6 @@ struct let op_scheme op1 op2 op3 op4 {cpa=x1; deps=x2; weak=x3; priv=x4} {cpa=y1; deps=y2; weak=y3; priv=y4}: t = {cpa = op1 x1 y1; deps = op2 x2 y2; weak = op3 x3 y3; priv = op4 x4 y4 } - let join = op_scheme CPA.join PartDeps.join WeakUpdates.join PrivD.join - let meet = op_scheme CPA.meet PartDeps.meet WeakUpdates.meet PrivD.meet - let widen = op_scheme CPA.widen PartDeps.widen WeakUpdates.widen PrivD.widen - let narrow = op_scheme CPA.narrow PartDeps.narrow WeakUpdates.narrow PrivD.narrow - - let relift {cpa; deps; weak; priv} = - {cpa = CPA.relift cpa; deps = PartDeps.relift deps; weak = WeakUpdates.relift weak; priv = PrivD.relift priv} end module type ExpEvaluator = diff --git a/src/cdomains/lockDomain.ml b/src/cdomains/lockDomain.ml index 35d73e5f28..b71573d6f6 100644 --- a/src/cdomains/lockDomain.ml +++ b/src/cdomains/lockDomain.ml @@ -24,6 +24,8 @@ struct else Some false + let of_var v: t = (v, `NoOffset) + let of_mval ((v, o): Mval.t): t = (v, Offset.Poly.map_indices (fun i -> IndexDomain.to_int i |> Option.get) o) @@ -40,7 +42,7 @@ struct end (* true means exclusive lock and false represents reader lock*) -module RW = IntDomain.Booleans +module RW = BoolDomain.MayBool (* TODO: name booleans? *) (* pair Addr and RW; also change pretty printing*) module MakeRW (P: Printable.S) = diff --git a/src/cdomains/mHP.ml b/src/cdomains/mHP.ml index f58b7d2e92..433486d4e0 100644 --- a/src/cdomains/mHP.ml +++ b/src/cdomains/mHP.ml @@ -11,10 +11,7 @@ type t = { tid: ThreadIdDomain.ThreadLifted.t; created: ConcDomain.ThreadSet.t; must_joined: ConcDomain.ThreadSet.t; -} [@@deriving eq, ord, hash] - -let relift {tid; created; must_joined} = - {tid = ThreadIdDomain.ThreadLifted.relift tid; created = ConcDomain.ThreadSet.relift created; must_joined = ConcDomain.ThreadSet.relift must_joined} +} [@@deriving eq, ord, hash, relift] let current (ask:Queries.ask) = { diff --git a/src/cdomains/pthreadDomain.ml b/src/cdomains/pthreadDomain.ml index 2c12689dcf..8862a768bc 100644 --- a/src/cdomains/pthreadDomain.ml +++ b/src/cdomains/pthreadDomain.ml @@ -25,7 +25,7 @@ end module D = struct include Printable.StdLeaf - type t = { tid : Tid.t; pred : Pred.t; ctx : Ctx.t } [@@deriving eq, ord, hash, to_yojson] + type t = { tid : Tid.t; pred : Pred.t; ctx : Ctx.t } [@@deriving eq, ord, hash, relift, to_yojson, lattice] (** printing *) let show x = @@ -35,26 +35,15 @@ module D = struct (Pred.show x.pred) (Ctx.show x.ctx) - include Printable.SimpleShow(struct type nonrec t = t let show = show end) (* TODO: overrides derived to_yojson *) + include Printable.SimpleShow (struct + type nonrec t = t + let show = show + end) (* TODO: overrides derived to_yojson *) let name () = "pthread state" let make tid pred ctx = { tid; pred; ctx } - let bot () = { tid = Tid.bot (); pred = Pred.bot (); ctx = Ctx.bot () } - let is_bot x = Tid.is_bot x.tid && Pred.is_bot x.pred && Ctx.is_bot x.ctx let any_is_bot x = Tid.is_bot x.tid || Pred.is_bot x.pred - let top () = { tid = Tid.top (); pred = Pred.top (); ctx = Ctx.top () } - let is_top x = Tid.is_top x.tid && Pred.is_top x.pred && Ctx.is_top x.ctx - - let leq x y = Tid.leq x.tid y.tid && Pred.leq x.pred y.pred && Ctx.leq x.ctx y.ctx - - let op_scheme op1 op2 op3 x y : t = - { tid = op1 x.tid y.tid; pred = op2 x.pred y.pred; ctx = op3 x.ctx y.ctx } - - let join = op_scheme Tid.join Pred.join Ctx.join - let widen = join - let meet = op_scheme Tid.meet Pred.meet Ctx.meet - let narrow = meet let pretty_diff () (x,y) = if not (Tid.leq x.tid y.tid) then diff --git a/src/cdomains/threadFlagDomain.ml b/src/cdomains/threadFlagDomain.ml index 80ba9b7a52..42571656e7 100644 --- a/src/cdomains/threadFlagDomain.ml +++ b/src/cdomains/threadFlagDomain.ml @@ -15,10 +15,11 @@ module Trivial: S = struct module TrivialNames = struct - let truename = "Multithreaded" - let falsename = "Singlethreaded" + let name = "MT mode" + let true_name = "Multithreaded" + let false_name = "Singlethreaded" end - include IntDomain.MakeBooleans (TrivialNames) + include BoolDomain.MakeMayBool (TrivialNames) let is_multi x = x let is_not_main x = x diff --git a/src/common/cdomains/basetype.ml b/src/common/cdomains/basetype.ml index 1b846309aa..bf832b1c3c 100644 --- a/src/common/cdomains/basetype.ml +++ b/src/common/cdomains/basetype.ml @@ -33,17 +33,6 @@ struct let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape (show x)) end -module RawBools: Printable.S with type t = bool = -struct - include Printable.StdLeaf - open Pretty - type t = bool [@@deriving eq, ord, hash, to_yojson] - let show (x:t) = if x then "true" else "false" - let pretty () x = text (show x) - let name () = "raw bools" - let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (show x) -end - module CilExp = struct include CilType.Exp diff --git a/src/common/domains/printable.ml b/src/common/domains/printable.ml index 1a642c932a..93d3f99edc 100644 --- a/src/common/domains/printable.ml +++ b/src/common/domains/printable.ml @@ -88,6 +88,20 @@ struct let to_yojson x = `String (show x) end +module type Formatable = +sig + type t + val pp: Format.formatter -> t -> unit +end + +module SimpleFormat (P: Formatable) = +struct + let show x = GobFormat.asprint P.pp x + let pretty () x = text (show x) + let printXml f x = BatPrintf.fprintf f "\n\n%s\n\n\n" (XmlUtil.escape (show x)) + let to_yojson x = `String (show x) +end + module type Name = sig val name: string end module UnitConf (N: Name) = @@ -465,7 +479,7 @@ module ProdConf (C: ProdConfiguration) (Base1: S) (Base2: S)= struct include C - type t = Base1.t * Base2.t [@@deriving eq, ord, hash] + type t = Base1.t * Base2.t [@@deriving eq, ord, hash, relift] include Std @@ -504,8 +518,6 @@ struct `Assoc [ (Base1.name (), Base1.to_yojson x); (Base2.name (), Base2.to_yojson y) ] let arbitrary () = QCheck.pair (Base1.arbitrary ()) (Base2.arbitrary ()) - - let relift (x,y) = (Base1.relift x, Base2.relift y) end module Prod = ProdConf (struct let expand_fst = true let expand_snd = true end) @@ -513,7 +525,7 @@ module ProdSimple = ProdConf (struct let expand_fst = false let expand_snd = fal module Prod3 (Base1: S) (Base2: S) (Base3: S) = struct - type t = Base1.t * Base2.t * Base3.t [@@deriving eq, ord, hash] + type t = Base1.t * Base2.t * Base3.t [@@deriving eq, ord, hash, relift] include Std let show (x,y,z) = @@ -555,40 +567,10 @@ struct let name () = Base1.name () ^ " * " ^ Base2.name () ^ " * " ^ Base3.name () - let relift (x,y,z) = (Base1.relift x, Base2.relift y, Base3.relift z) let arbitrary () = QCheck.triple (Base1.arbitrary ()) (Base2.arbitrary ()) (Base3.arbitrary ()) end -module Prod4 (Base1: S) (Base2: S) (Base3: S) (Base4: S) = struct - type t = Base1.t * Base2.t * Base3.t * Base4.t [@@deriving eq, ord, hash] - include Std - - let show (x,y,z,w) = "(" ^ Base1.show x ^ ", " ^ Base2.show y ^ ", " ^ Base3.show z ^ ", " ^ Base4.show w ^ ")" - - let pretty () (x,y,z,w) = - text "(" ++ - Base1.pretty () x - ++ text ", " ++ - Base2.pretty () y - ++ text ", " ++ - Base3.pretty () z - ++ text ", " ++ - Base4.pretty () w - ++ text ")" - - let printXml f (x,y,z,w) = - BatPrintf.fprintf f "\n\n\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n%s\n\n%a\n\n" (XmlUtil.escape (Base1.name ())) Base1.printXml x (XmlUtil.escape (Base2.name ())) Base2.printXml y (XmlUtil.escape (Base3.name ())) Base3.printXml z (XmlUtil.escape (Base4.name ())) Base4.printXml w - - let to_yojson (x, y, z, w) = - `Assoc [ (Base1.name (), Base1.to_yojson x); (Base2.name (), Base2.to_yojson y); (Base3.name (), Base3.to_yojson z); (Base4.name (), Base4.to_yojson w) ] - - let name () = Base1.name () ^ " * " ^ Base2.name () ^ " * " ^ Base3.name () ^ " * " ^ Base4.name () - - let relift (x,y,z,w) = (Base1.relift x, Base2.relift y, Base3.relift z, Base4.relift w) - let arbitrary () = QCheck.quad (Base1.arbitrary ()) (Base2.arbitrary ()) (Base3.arbitrary ()) (Base4.arbitrary ()) -end - -module PQueue (Base: S) = +module PQueue (Base: S) = struct type t = Base.t BatDeque.dq include Std @@ -604,22 +586,22 @@ struct let rec loop n q = match BatDeque.front q with | None -> () - | Some (x, xs) -> (BatPrintf.fprintf f "%d\n%a\n" n Base.printXml x; + | Some (x, xs) -> (BatPrintf.fprintf f "%d\n%a\n" n Base.printXml x; loop (n+1) (xs)) in BatPrintf.fprintf f "\n\n"; - loop 0 xs; + loop 0 xs; BatPrintf.fprintf f "\n\n" let to_yojson q = `List (BatDeque.to_list @@ BatDeque.map (Base.to_yojson) q) let hash q = BatDeque.fold_left (fun acc x -> (acc + 71) * (Base.hash x)) 11 q let equal q1 q2 = BatDeque.eq ~eq:Base.equal q1 q2 - let compare q1 q2 = + let compare q1 q2 = match BatDeque.front q1, BatDeque.front q2 with | None, None -> 0 | None, Some(_, _) -> -1 | Some(_, _), None -> 1 - | Some(a1, q1'), Some(a2, q2') -> + | Some(a1, q1'), Some(a2, q2') -> let c = Base.compare a1 a2 in if c <> 0 then c else compare q1' q2' diff --git a/src/common/dune b/src/common/dune index 0cded0bdf9..e5aeddde7f 100644 --- a/src/common/dune +++ b/src/common/dune @@ -11,6 +11,7 @@ goblint_logs goblint_config goblint_tracing + goblint_backtrace goblint-cil fpath yojson @@ -23,7 +24,8 @@ (pps ppx_deriving.std ppx_deriving_hash - ppx_deriving_yojson)) + ppx_deriving_yojson + ppx_deriving_printable)) (instrumentation (backend bisect_ppx))) (documentation) diff --git a/src/common/framework/analysisState.ml b/src/common/framework/analysisState.ml index d1764c839f..96816b8529 100644 --- a/src/common/framework/analysisState.ml +++ b/src/common/framework/analysisState.ml @@ -36,3 +36,8 @@ let postsolving = ref false (* None if verification is disabled, Some true if verification succeeded, Some false if verification failed *) let verified : bool option ref = ref None + +let unsound_both_branches_dead: bool option ref = ref None +(** [Some true] if unsound both branches dead occurs in analysis results. + [Some false] if it doesn't occur. + [None] if [ana.dead-code.branches] option is disabled and this isn't checked. *) \ No newline at end of file diff --git a/src/common/util/cilType.ml b/src/common/util/cilType.ml index a484a228bd..91368052b3 100644 --- a/src/common/util/cilType.ml +++ b/src/common/util/cilType.ml @@ -79,8 +79,8 @@ struct let of_yojson = function | `Assoc l -> - begin match List.assoc_opt "file" l, List.assoc_opt "line" l, List.assoc_opt "column" l, List.assoc_opt "byte" l with - | Some (`String file), Some (`Int line), Some (`Int column), Some (`Int byte) -> + begin match List.assoc_opt "file" l, List.assoc_opt "line" l, List.assoc_opt "column" l, Option.value ~default:(`Int (-1)) (List.assoc_opt "byte" l) with + | Some (`String file), Some (`Int line), Some (`Int column), `Int byte -> let loc = {file; line; column; byte; endLine = -1; endColumn = -1; endByte = -1; synthetic = false} in begin match List.assoc_opt "endLine" l, List.assoc_opt "endColumn" l, List.assoc_opt "endByte" l with | Some (`Int endLine), Some (`Int endColumn), Some (`Int endByte) -> @@ -698,6 +698,7 @@ struct | AAddrOf of t | AIndex of t * t | AQuestion of t * t * t + | AAssign of t * t [@@deriving eq, ord, hash] let name () = "attrparam" diff --git a/src/common/util/cilfacade.ml b/src/common/util/cilfacade.ml index 99430ee8b6..6e86701858 100644 --- a/src/common/util/cilfacade.ml +++ b/src/common/util/cilfacade.ml @@ -6,6 +6,14 @@ module E = Errormsg include Cilfacade0 +type Goblint_backtrace.mark += FunVarinfo of varinfo + +let () = Goblint_backtrace.register_mark_printer (function + | FunVarinfo var -> + Some ("function " ^ CilType.Varinfo.show var) + | _ -> None (* for other marks *) + ) + (** Command for assigning an id to a varinfo. All varinfos directly created by Goblint should be modified by this method *) let create_var (var: varinfo) = (* Hack: using negative integers should preempt conflicts with ids generated by CIL *) @@ -39,7 +47,20 @@ let init_options () = Mergecil.merge_inlines := get_bool "cil.merge.inlines"; Cil.cstd := Cil.cstd_of_string (get_string "cil.cstd"); Cil.gnu89inline := get_bool "cil.gnu89inline"; - Cabs2cil.addNestedScopeAttr := get_bool "cil.addNestedScopeAttr" + Cabs2cil.addNestedScopeAttr := get_bool "cil.addNestedScopeAttr"; + + if get_bool "ana.sv-comp.enabled" then ( + let machine = match get_string "exp.architecture" with + | "32bit" -> Machdep.gcc32 + | "64bit" -> Machdep.gcc64 + | _ -> assert false + in + match machine with + | Some _ -> Cil.envMachine := machine + | None -> + GobRef.wrap AnalysisState.should_warn true (fun () -> Messages.msg_final Error ~category:Unsound "Machine definition not available for selected architecture"); + Logs.error "Machine definition not available for selected architecture, defaulting to host" + ) let init () = initCIL (); @@ -453,8 +474,8 @@ let rec pretty_typsig_like_typ (nameOpt: Pretty.doc option) () ts = (* ignore the const attribute for arrays *) let a' = dropAttributes [ "pconst" ] a in let name' = - if a' == [] then name else - if nameOpt == None then printAttributes a' else + if a' = [] then name else + if nameOpt = None then printAttributes a' else text "(" ++ printAttributes a' ++ name ++ text ")" in pretty_typsig_like_typ @@ -467,8 +488,8 @@ let rec pretty_typsig_like_typ (nameOpt: Pretty.doc option) () ts = | TSFun (restyp, args, isvararg, a) -> let name' = - if a == [] then name else - if nameOpt == None then printAttributes a else + if a = [] then name else + if nameOpt = None then printAttributes a else text "(" ++ printAttributes a ++ name ++ text ")" in pretty_typsig_like_typ @@ -734,7 +755,7 @@ let add_function_declarations (file: Cil.file): unit = let functions, non_functions = List.partition (fun g -> match g with GFun _ -> true | _ -> false) globals in let upto_last_type, non_types = GobList.until_last_with (fun g -> match g with GType _ -> true | _ -> false) non_functions in let declaration_from_GFun f = match f with - | GFun (f, _) when BatString.starts_with_stdlib ~prefix:"__builtin" f.svar.vname -> + | GFun (f, _) when String.starts_with ~prefix:"__builtin" f.svar.vname -> (* Builtin functions should not occur in asserts generated, so there is no need to add declarations for them.*) None | GFun (f, _) -> diff --git a/src/common/util/gobFormat.ml b/src/common/util/gobFormat.ml index 3cda0a4758..8f26ff0087 100644 --- a/src/common/util/gobFormat.ml +++ b/src/common/util/gobFormat.ml @@ -19,3 +19,12 @@ let pp_set_ansi_color_tags ppf = Format.pp_set_mark_tags ppf true let pp_print_nothing (ppf: Format.formatter) () = () + +let pp_infinity = 1000000001 (* Exact value not exposed before OCaml 5.2, but use the smallest value permitted by documentation. *) + +let pp_set_infinite_geometry = Format.pp_set_geometry ~max_indent:(pp_infinity - 2) ~margin:(pp_infinity - 1) + +let asprintf (fmt: ('a, Format.formatter, unit, string) format4): 'a = + Format.asprintf ("%t" ^^ fmt) pp_set_infinite_geometry + +let asprint pp x = asprintf "%a" pp x (* eta-expanded to bypass value restriction *) diff --git a/src/config/options.schema.json b/src/config/options.schema.json index acf85abed9..447290b44d 100644 --- a/src/config/options.schema.json +++ b/src/config/options.schema.json @@ -79,9 +79,9 @@ "result": { "title": "result", "description": - "Result style: none, fast_xml, json, pretty, json-messages, sarif.", + "Result style: none, fast_xml, json, pretty, pretty-deterministic, json-messages, sarif.", "type": "string", - "enum": ["none", "fast_xml", "json", "pretty", "json-messages", "sarif"], + "enum": ["none", "fast_xml", "json", "pretty", "pretty-deterministic", "json-messages", "sarif"], "default": "none" }, "solver": { @@ -344,7 +344,7 @@ "default": [ "expRelation", "base", "threadid", "threadflag", "threadreturn", "escape", "mutexEvents", "mutex", "access", "race", "mallocWrapper", "mhp", - "assert" + "assert", "pthreadMutexType" ] }, "path_sens": { @@ -807,6 +807,20 @@ "type": "string", "enum": ["once", "fixpoint"], "default": "once" + }, + "int": { + "title": "ana.base.invariant.int", + "type": "object", + "properties": { + "simplify": { + "title": "ana.base.invariant.int.simplify", + "description": "How much to simplify int domain invariants. Value \"int\" only simplifies definite integers. Without int domain refinement \"all\" might not be maximally precise.", + "type": "string", + "enum": ["none", "int", "all"], + "default": "all" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -984,6 +998,14 @@ "type": "integer", "default": -1 }, + "gas_scope": { + "title": "ana.context.gas_scope", + "description": + "Whether the gas should be 'global' (default) or per 'function'", + "type": "string", + "enum": ["global","function"], + "default": "global" + }, "callString_length": { "title": "ana.context.callString_length", "description": "Length of the call string that should be used as context for the call_string and/or call_site analyses. In case the value is zero, the analysis is context-insensitive. For a negative value, an infinite call string is used! For this option to have an effect, one of the analyses in `callstring.ml` must be activated.", @@ -1561,6 +1583,26 @@ "description": "Output filename for transformations that output a transformed file.", "type":"string", "default": "transformed.c" + }, + "assert" : { + "title": "trans.assert", + "type": "object", + "properties": { + "function": { + "title": "trans.assert.function", + "description": "Function to use for assertions in output.", + "type": "string", + "enum": ["assert", "__goblint_check", "__VERIFIER_assert"], + "default": "__VERIFIER_assert" + }, + "wrap-atomic": { + "title": "trans.assert.wrap-atomic", + "description": "Wrap assertions in __VERIFIER_atomic_begin and __VERIFIER_atomic_end.", + "type": "boolean", + "default": true + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2574,7 +2616,8 @@ "precondition_loop_invariant", "loop_invariant_certificate", "precondition_loop_invariant_certificate", - "invariant_set" + "invariant_set", + "violation_sequence" ] }, "default": [ diff --git a/src/domain/boolDomain.ml b/src/domain/boolDomain.ml index d92d716d7a..77fcf7e108 100644 --- a/src/domain/boolDomain.ml +++ b/src/domain/boolDomain.ml @@ -1,31 +1,65 @@ (** Boolean domains. *) -module Bool = +module type Names = +sig + val name: string + val true_name: string + val false_name: string +end + +module MakeBool (Names: Names) = struct - include Basetype.RawBools - (* type t = bool - let equal = Bool.equal - let compare = Bool.compare - let relift x = x - let arbitrary () = QCheck.bool *) + include Printable.StdLeaf + type t = bool [@@deriving eq, ord, hash] + let name () = Names.name + + let show x = if x then Names.true_name else Names.false_name + include Printable.SimpleShow (struct + type nonrec t = t + let show = show + end) + + let arbitrary () = QCheck.bool + (* For Lattice.S *) let pretty_diff () (x,y) = GoblintCil.Pretty.dprintf "%s: %a not leq %a" (name ()) pretty x pretty y end -module MayBool: Lattice.S with type t = bool = +module StdNames: Names = struct - include Bool + let name = "bool" + let true_name = "true" + let false_name = "false" +end + +module Bool = +struct + include MakeBool (StdNames) + let to_yojson = [%to_yojson: bool] (* override to_yojson from SimpleShow *) +end + +module MakeMayBool (Names: Names): Lattice.S with type t = bool = +struct + include MakeBool (Names) let bot () = false let is_bot x = x = false let top () = true let is_top x = x = true - let leq x y = x == y || y + let leq x y = (x = y) || y let join = (||) let widen = (||) let meet = (&&) let narrow = (&&) end +module MayBool: Lattice.S with type t = bool = +struct + include MakeMayBool (StdNames) + let to_yojson = [%to_yojson: bool] (* override to_yojson from SimpleShow *) +end + +(* TODO: MakeMustBool? *) + module MustBool: Lattice.S with type t = bool = struct include Bool @@ -33,7 +67,7 @@ struct let is_bot x = x = true let top () = false let is_top x = x = false - let leq x y = x == y || x + let leq x y = (x = y) || x let join = (&&) let widen = (&&) let meet = (||) diff --git a/src/domain/dune b/src/domain/dune index 85e69a6246..ce4cdcb1a5 100644 --- a/src/domain/dune +++ b/src/domain/dune @@ -14,7 +14,8 @@ (pps ppx_deriving.std ppx_deriving_hash - ppx_deriving_yojson)) + ppx_deriving_yojson + ppx_deriving_lattice)) (instrumentation (backend bisect_ppx))) (documentation) diff --git a/src/domain/lattice.ml b/src/domain/lattice.ml index 99322c09d8..37a4a2fef5 100644 --- a/src/domain/lattice.ml +++ b/src/domain/lattice.ml @@ -153,11 +153,11 @@ struct include Printable.HConsed (Base) let lift_f2 f x y = f (unlift x) (unlift y) - let narrow x y = if Arg.assume_idempotent && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.narrow x y) - let widen x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.widen x y) - let meet x y = if Arg.assume_idempotent && x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.meet x y) - let join x y = if x.BatHashcons.tag == y.BatHashcons.tag then x else lift (lift_f2 Base.join x y) - let leq x y = (x.BatHashcons.tag == y.BatHashcons.tag) || lift_f2 Base.leq x y + let narrow x y = if Arg.assume_idempotent && x.BatHashcons.tag = y.BatHashcons.tag then x else lift (lift_f2 Base.narrow x y) + let widen x y = if x.BatHashcons.tag = y.BatHashcons.tag then x else lift (lift_f2 Base.widen x y) + let meet x y = if Arg.assume_idempotent && x.BatHashcons.tag = y.BatHashcons.tag then x else lift (lift_f2 Base.meet x y) + let join x y = if x.BatHashcons.tag = y.BatHashcons.tag then x else lift (lift_f2 Base.join x y) + let leq x y = (x.BatHashcons.tag = y.BatHashcons.tag) || lift_f2 Base.leq x y let is_top = lift_f Base.is_top let is_bot = lift_f Base.is_bot let top () = lift (Base.top ()) @@ -277,6 +277,8 @@ struct let narrow x y = match (x,y) with | (`Lifted x, `Lifted y) -> `Lifted (Base.narrow x y) + | (_, `Bot) -> `Bot + | (`Top, y) -> y | _ -> x end @@ -337,6 +339,8 @@ struct | (`Lifted x, `Lifted y) -> (try `Lifted (Base.narrow x y) with Uncomparable -> `Bot) + | (_, `Bot) -> `Bot + | (`Top, y) -> y | _ -> x end @@ -408,6 +412,8 @@ struct match (x,y) with | (`Lifted1 x, `Lifted1 y) -> `Lifted1 (Base1.narrow x y) | (`Lifted2 x, `Lifted2 y) -> `Lifted2 (Base2.narrow x y) + | (_, `Bot) -> `Bot + | (`Top, y) -> y | _ -> x end @@ -416,27 +422,17 @@ module Lift2 = Lift2Conf (Printable.DefaultConf) module ProdConf (C: Printable.ProdConfiguration) (Base1: S) (Base2: S) = struct - include Printable.ProdConf (C) (Base1) (Base2) - - let bot () = (Base1.bot (), Base2.bot ()) - let is_bot (x1,x2) = Base1.is_bot x1 && Base2.is_bot x2 - let top () = (Base1.top (), Base2.top ()) - let is_top (x1,x2) = Base1.is_top x1 && Base2.is_top x2 - - let leq (x1,x2) (y1,y2) = Base1.leq x1 y1 && Base2.leq x2 y2 + open struct (* open to avoid leaking P and causing conflicts *) + module P = Printable.ProdConf (C) (Base1) (Base2) + end + type t = Base1.t * Base2.t [@@deriving lattice] + include (P: module type of P with type t := t) let pretty_diff () ((x1,x2:t),(y1,y2:t)): Pretty.doc = if Base1.leq x1 y1 then Base2.pretty_diff () (x2,y2) else Base1.pretty_diff () (x1,y1) - - let op_scheme op1 op2 (x1,x2) (y1,y2): t = (op1 x1 y1, op2 x2 y2) - let join = op_scheme Base1.join Base2.join - let meet = op_scheme Base1.meet Base2.meet - let narrow = op_scheme Base1.narrow Base2.narrow - let widen = op_scheme Base1.widen Base2.widen - end @@ -445,14 +441,11 @@ module ProdSimple = ProdConf (struct let expand_fst = false let expand_snd = fal module Prod3 (Base1: S) (Base2: S) (Base3: S) = struct - include Printable.Prod3 (Base1) (Base2) (Base3) - - let bot () = (Base1.bot (), Base2.bot (), Base3.bot ()) - let is_bot (x1,x2,x3) = Base1.is_bot x1 && Base2.is_bot x2 && Base3.is_bot x3 - let top () = (Base1.top (), Base2.top (), Base3.top ()) - let is_top (x1,x2,x3) = Base1.is_top x1 && Base2.is_top x2 && Base3.is_top x3 - - let leq (x1,x2,x3) (y1,y2,y3) = Base1.leq x1 y1 && Base2.leq x2 y2 && Base3.leq x3 y3 + open struct (* open to avoid leaking P and causing conflicts *) + module P = Printable.Prod3 (Base1) (Base2) (Base3) + end + type t = Base1.t * Base2.t * Base3.t [@@deriving lattice] + include (P: module type of P with type t := t) let pretty_diff () ((x1,x2,x3:t),(y1,y2,y3:t)): Pretty.doc = if not (Base1.leq x1 y1) then @@ -461,39 +454,6 @@ struct Base2.pretty_diff () (x2,y2) else Base3.pretty_diff () (x3,y3) - - let op_scheme op1 op2 op3 (x1,x2,x3) (y1,y2,y3): t = (op1 x1 y1, op2 x2 y2, op3 x3 y3) - let join = op_scheme Base1.join Base2.join Base3.join - let meet = op_scheme Base1.meet Base2.meet Base3.meet - let widen = op_scheme Base1.widen Base2.widen Base3.widen - let narrow = op_scheme Base1.narrow Base2.narrow Base3.narrow -end - -module Prod4 (Base1: S) (Base2: S) (Base3: S) (Base4: S) = -struct - include Printable.Prod4 (Base1) (Base2) (Base3) (Base4) - - let bot () = (Base1.bot (), Base2.bot (), Base3.bot (), Base4.bot ()) - let is_bot (x1,x2,x3,x4) = Base1.is_bot x1 && Base2.is_bot x2 && Base3.is_bot x3 && Base4.is_bot x4 - let top () = (Base1.top (), Base2.top (), Base3.top (), Base4.top ()) - let is_top (x1,x2,x3,x4) = Base1.is_top x1 && Base2.is_top x2 && Base3.is_top x3 && Base4.is_top x4 - let leq (x1,x2,x3,x4) (y1,y2,y3,y4) = Base1.leq x1 y1 && Base2.leq x2 y2 && Base3.leq x3 y3 && Base4.leq x4 y4 - - let pretty_diff () ((x1,x2,x3,x4:t),(y1,y2,y3,y4:t)): Pretty.doc = - if not (Base1.leq x1 y1) then - Base1.pretty_diff () (x1,y1) - else if not (Base2.leq x2 y2) then - Base2.pretty_diff () (x2,y2) - else if not (Base3.leq x3 y3) then - Base3.pretty_diff () (x3,y3) - else - Base4.pretty_diff () (x4,y4) - - let op_scheme op1 op2 op3 op4 (x1,x2,x3,x4) (y1,y2,y3,y4): t = (op1 x1 y1, op2 x2 y2, op3 x3 y3, op4 x4 y4) - let join = op_scheme Base1.join Base2.join Base3.join Base4.join - let meet = op_scheme Base1.meet Base2.meet Base3.meet Base4.meet - let widen = op_scheme Base1.widen Base2.widen Base3.widen Base4.widen - let narrow = op_scheme Base1.narrow Base2.narrow Base3.narrow Base4.narrow end module LiftBot (Base : S) = @@ -539,6 +499,7 @@ struct let narrow x y = match (x,y) with | (`Lifted x, `Lifted y) -> `Lifted (Base.narrow x y) + | (_, `Bot) -> `Bot | _ -> x end @@ -580,6 +541,7 @@ struct let narrow x y = match (x,y) with | (`Lifted x, `Lifted y) -> `Lifted (Base.narrow x y) + | (`Top, y) -> y | _ -> x let pretty_diff () (x,y) = diff --git a/src/domain/setDomain.ml b/src/domain/setDomain.ml index 9b545a78ee..c552363f3d 100644 --- a/src/domain/setDomain.ml +++ b/src/domain/setDomain.ml @@ -184,7 +184,7 @@ struct end ) - let hash x = fold (fun x y -> y + Base.hash x) x 0 + let hash x = fold (fun x y -> 13 * y + Base.hash x) x 0 let relift x = map Base.relift x diff --git a/src/domains/access.ml b/src/domains/access.ml index c35fb3a16d..f7ce68a18b 100644 --- a/src/domains/access.ml +++ b/src/domains/access.ml @@ -1,6 +1,5 @@ (** Memory accesses and their manipulation. *) -open Batteries open GoblintCil open Pretty open GobConfig @@ -12,7 +11,7 @@ module M = Messages let is_ignorable_comp_name = function | "__pthread_mutex_s" | "__pthread_rwlock_arch_t" | "__jmp_buf_tag" | "_pthread_cleanup_buffer" | "__pthread_cleanup_frame" | "__cancel_jmp_buf_tag" | "_IO_FILE" -> true - | cname when String.starts_with_stdlib ~prefix:"__anon" cname -> + | cname when String.starts_with ~prefix:"__anon" cname -> begin match Cilfacade.split_anoncomp_name cname with | (true, Some ("__once_flag" | "__pthread_unwind_buf_t" | "__cancel_jmp_buf"), _) -> true (* anonstruct *) | (false, Some ("pthread_mutexattr_t" | "pthread_condattr_t" | "pthread_barrierattr_t"), _) -> true (* anonunion *) @@ -385,7 +384,7 @@ and distribute_access_exp f = function and distribute_access_type f = function | TArray (et, len, _) -> - Option.may (distribute_access_exp f) len; + Option.iter (distribute_access_exp f) len; distribute_access_type f et | TVoid _ @@ -434,7 +433,7 @@ struct include SetDomain.Make (A) let max_conf accs = - accs |> elements |> List.map (fun {A.conf; _} -> conf) |> (List.max ~cmp:Int.compare) + accs |> elements |> List.map (fun {A.conf; _} -> conf) |> (BatList.max ~cmp:Int.compare) end @@ -583,7 +582,7 @@ let incr_summary ~safe ~vulnerable ~unsafe grouped_accs = |> List.filter_map race_conf |> (function | [] -> None - | confs -> Some (List.max confs) + | confs -> Some (BatList.max confs) ) in match safety with diff --git a/src/domains/queries.ml b/src/domains/queries.ml index 1ceb91ef1d..b0ede0cfbf 100644 --- a/src/domains/queries.ml +++ b/src/domains/queries.ml @@ -49,8 +49,8 @@ end (* Helper definitions for deriving complex parts of Any.compare below. *) type maybepublic = {global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash] -type maybepublicwithout = {global: CilType.Varinfo.t; write: bool; without_mutex: PreValueDomain.Addr.t; protection: Protection.t} [@@deriving ord, hash] -type mustbeprotectedby = {mutex: PreValueDomain.Addr.t; global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash] +type maybepublicwithout = {global: CilType.Varinfo.t; write: bool; without_mutex: LockDomain.MustLock.t; protection: Protection.t} [@@deriving ord, hash] +type mustbeprotectedby = {mutex: LockDomain.MustLock.t; global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash] type mustprotectedvars = {mutex: LockDomain.MustLock.t; write: bool} [@@deriving ord, hash] type access = | Memory of {exp: CilType.Exp.t; var_opt: CilType.Varinfo.t option; kind: AccessKind.t} (** Memory location access (race). *) @@ -124,9 +124,9 @@ type _ t = | MustTermLoop: stmt -> MustBool.t t | MustTermAllLoops: MustBool.t t | IsEverMultiThreaded: MayBool.t t - | TmpSpecial: Mval.Exp.t -> ML.t t + | TmpSpecial: Mval.Exp.t -> ML.t t | MaySignedOverflow: exp -> MayBool.t t - | GasExhausted: MustBool.t t + | GasExhausted: CilType.Fundec.t -> MustBool.t t type 'a result = 'a @@ -197,7 +197,7 @@ struct | IsEverMultiThreaded -> (module MayBool) | TmpSpecial _ -> (module ML) | MaySignedOverflow _ -> (module MayBool) - | GasExhausted -> (module MustBool) + | GasExhausted _ -> (module MustBool) (** Get bottom result for query. *) let bot (type a) (q: a t): a result = @@ -267,7 +267,7 @@ struct | IsEverMultiThreaded -> MayBool.top () | TmpSpecial _ -> ML.top () | MaySignedOverflow _ -> MayBool.top () - | GasExhausted -> MustBool.top () + | GasExhausted _ -> MustBool.top () end (* The type any_query can't be directly defined in Any as t, @@ -334,7 +334,7 @@ struct | Any (TmpSpecial _) -> 56 | Any (IsAllocVar _) -> 57 | Any (MaySignedOverflow _) -> 58 - | Any GasExhausted -> 59 + | Any (GasExhausted _) -> 59 let rec compare a b = let r = Stdlib.compare (order a) (order b) in @@ -389,6 +389,7 @@ struct | Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2 | Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2 | Any (MaySignedOverflow e1), Any (MaySignedOverflow e2) -> CilType.Exp.compare e1 e2 + | Any (GasExhausted f1), Any (GasExhausted f2) -> CilType.Fundec.compare f1 f2 (* only argumentless queries should remain *) | _, _ -> Stdlib.compare (order a) (order b) @@ -431,6 +432,7 @@ struct | Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start | Any (TmpSpecial lv) -> Mval.Exp.hash lv | Any (MaySignedOverflow e) -> CilType.Exp.hash e + | Any (GasExhausted f) -> CilType.Fundec.hash f (* IterSysVars: *) (* - argument is a function and functions cannot be compared in any meaningful way. *) (* - doesn't matter because IterSysVars is always queried from outside of the analysis, so MCP's query caching is not done for it. *) @@ -494,7 +496,7 @@ struct | Any IsEverMultiThreaded -> Pretty.dprintf "IsEverMultiThreaded" | Any (TmpSpecial lv) -> Pretty.dprintf "TmpSpecial %a" Mval.Exp.pretty lv | Any (MaySignedOverflow e) -> Pretty.dprintf "MaySignedOverflow %a" CilType.Exp.pretty e - | Any GasExhausted -> Pretty.dprintf "GasExhausted" + | Any (GasExhausted f) -> Pretty.dprintf "GasExhausted %a" CilType.Fundec.pretty f end let to_value_domain_ask (ask: ask) = diff --git a/src/dune b/src/dune index 5265821b5a..a307a237ae 100644 --- a/src/dune +++ b/src/dune @@ -71,7 +71,7 @@ (flags :standard -open Goblint_std -open Goblint_logs) (ocamlopt_flags :standard -no-float-const-prop) (preprocess - (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson ppx_blob)) + (pps ppx_deriving.std ppx_deriving_hash ppx_deriving_yojson ppx_blob ppx_deriving_printable ppx_deriving_lattice)) (instrumentation (backend bisect_ppx)) ) diff --git a/src/framework/analyses.ml b/src/framework/analyses.ml index 1ea01c99fb..ab41335944 100644 --- a/src/framework/analyses.ml +++ b/src/framework/analyses.ml @@ -14,8 +14,7 @@ type fundecs = fundec list * fundec list * fundec list module Var = struct - type t = Node.t [@@deriving eq, ord, hash] - let relift = Node.relift + type t = Node.t [@@deriving eq, ord, hash, relift] let printXml f n = let l = Node.location n in @@ -209,7 +208,7 @@ sig val context: (D.t, G.t, C.t, V.t) ctx -> fundec -> D.t -> C.t val startcontext: unit -> C.t - val sync : (D.t, G.t, C.t, V.t) ctx -> [`Normal | `Join | `JoinCall | `Return] -> D.t + val sync : (D.t, G.t, C.t, V.t) ctx -> [`Normal | `Join | `JoinCall of CilType.Fundec.t | `Return] -> D.t val query : (D.t, G.t, C.t, V.t) ctx -> 'a Queries.t -> 'a Queries.result (** A transfer function which handles the assignment of a rval to a lval, i.e., @@ -275,6 +274,8 @@ sig val event : (D.t, G.t, C.t, V.t) ctx -> Events.t -> (D.t, G.t, C.t, V.t) ctx -> D.t end +module type Spec2Spec = functor (S: Spec) -> Spec + module type MCPA = sig include Printable.S diff --git a/src/framework/analysisResult.ml b/src/framework/analysisResult.ml index 41c2bbd2c7..c1d7fde8a5 100644 --- a/src/framework/analysisResult.ml +++ b/src/framework/analysisResult.ml @@ -45,6 +45,18 @@ struct let defline () = dprintf "OTHERS -> Not available\n" in dprintf "@[Mapping {\n @[%t%t@]}@]" content defline + let pretty_deterministic () mapping = + let bindings = + to_list mapping + |> List.sort [%ord: ResultNode.t * Range.t] + in + let f dok (key, st) = + dok ++ dprintf "%a ->@? @[%a@]\n" ResultNode.pretty key Range.pretty st + in + let content () = List.fold_left f nil bindings in + let defline () = dprintf "OTHERS -> Not available\n" in + dprintf "@[Mapping {\n @[%t%t@]}@]" content defline + include C let printXml f xs = @@ -91,6 +103,7 @@ struct let out = Messages.get_out result_name !Messages.out in match get_string "result" with | "pretty" -> ignore (fprintf out "%a\n" pretty (Lazy.force table)) + | "pretty-deterministic" -> ignore (fprintf out "%a\n" pretty_deterministic (Lazy.force table)) | "fast_xml" -> let module SH = BatHashtbl.Make (Basetype.RawStrings) in let file2funs = SH.create 100 in diff --git a/src/framework/compareConstraints.ml b/src/framework/compareConstraints.ml new file mode 100644 index 0000000000..5197ac8e4e --- /dev/null +++ b/src/framework/compareConstraints.ml @@ -0,0 +1,217 @@ +open Batteries +open Analyses +open ConstrSys +open GobConfig + + +module CompareGlobSys (SpecSys: SpecSys) = +struct + open SpecSys + module Sys = EQSys + module LH = LHT + module GH = GHT + + open Spec + module G = Sys.G + + module PP = Hashtbl.Make (Node) + + let compare_globals g1 g2 = + let eq, le, gr, uk = ref 0, ref 0, ref 0, ref 0 in + let f_eq () = incr eq in + let f_le () = incr le in + let f_gr () = incr gr in + let f_uk () = incr uk in + let f k v1 = + let v2 = try GH.find g2 k with Not_found -> G.bot () in + let b1 = G.leq v1 v2 in + let b2 = G.leq v2 v1 in + if b1 && b2 then + f_eq () + else if b1 then begin + if get_bool "dbg.compare_runs.diff" then + Logs.info "Global %a is more precise using left:\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v2,v1); + f_le () + end else if b2 then begin + if get_bool "dbg.compare_runs.diff" then + Logs.info "Global %a is more precise using right:\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v1,v2); + f_gr () + end else begin + if get_bool "dbg.compare_runs.diff" then ( + Logs.info "Global %a is incomparable (diff):\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v1,v2); + Logs.info "Global %a is incomparable (reverse diff):\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v2,v1); + ); + f_uk () + end + in + GH.iter f g1; + Logs.info "globals:\tequal = %d\tleft = %d\tright = %d\tincomparable = %d" !eq !le !gr !uk + + let compare_locals h1 h2 = + let eq, le, gr, uk = ref 0, ref 0, ref 0, ref 0 in + let f k v1 = + if PP.mem h2 k then + let v2 = PP.find h2 k in + let b1 = D.leq v1 v2 in + let b2 = D.leq v2 v1 in + if b1 && b2 then + incr eq + else if b1 then begin + if get_bool "dbg.compare_runs.diff" then + Logs.info "%a @@ %a is more precise using left:\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v2,v1); + incr le + end else if b2 then begin + if get_bool "dbg.compare_runs.diff" then + Logs.info "%a @@ %a is more precise using right:\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v1,v2); + incr gr + end else begin + if get_bool "dbg.compare_runs.diff" then ( + Logs.info "%a @@ %a is incomparable (diff):\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v1,v2); + Logs.info "%a @@ %a is incomparable (reverse diff):\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v2,v1); + ); + incr uk + end + in + PP.iter f h1; + (* let k1 = Set.of_enum @@ PP.keys h1 in + let k2 = Set.of_enum @@ PP.keys h2 in + let o1 = Set.cardinal @@ Set.diff k1 k2 in + let o2 = Set.cardinal @@ Set.diff k2 k1 in + Logs.info "locals: \tequal = %d\tleft = %d[%d]\tright = %d[%d]\tincomparable = %d" !eq !le o1 !gr o2 !uk *) + Logs.info "locals: \tequal = %d\tleft = %d\tright = %d\tincomparable = %d" !eq !le !gr !uk + + let compare_locals_ctx h1 h2 = + let eq, le, gr, uk, no2, no1 = ref 0, ref 0, ref 0, ref 0, ref 0, ref 0 in + let f_eq () = incr eq in + let f_le () = incr le in + let f_gr () = incr gr in + let f_uk () = incr uk in + let f k v1 = + if not (LH.mem h2 k) then incr no2 else + let v2 = LH.find h2 k in + let b1 = D.leq v1 v2 in + let b2 = D.leq v2 v1 in + if b1 && b2 then + f_eq () + else if b1 then begin + if get_bool "dbg.compare_runs.diff" then + Logs.info "%a is more precise using left:\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v2,v1); + f_le () + end else if b2 then begin + if get_bool "dbg.compare_runs.diff" then + Logs.info "%a is more precise using right:\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v1,v2); + f_gr () + end else begin + if get_bool "dbg.compare_runs.diff" then ( + Logs.info "%a is incomparable (diff):\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v1,v2); + Logs.info "%a is incomparable (reverse diff):\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v2,v1); + ); + f_uk () + end + in + LH.iter f h1; + let f k v2 = + if not (LH.mem h1 k) then incr no1 + in + LH.iter f h2; + (* let k1 = Set.of_enum @@ PP.keys h1 in *) + (* let k2 = Set.of_enum @@ PP.keys h2 in *) + (* let o1 = Set.cardinal @@ Set.diff k1 k2 in *) + (* let o2 = Set.cardinal @@ Set.diff k2 k1 in *) + Logs.info "locals_ctx:\tequal = %d\tleft = %d\tright = %d\tincomparable = %d\tno_ctx_in_right = %d\tno_ctx_in_left = %d" !eq !le !gr !uk !no2 !no1 + + let compare (name1,name2) (l1,g1) (l2,g2) = + let one_ctx (n,_) v h = + PP.replace h n (try D.join v (PP.find h n) with Not_found -> v); + h + in + (* these contain results where the contexts per node have been joined *) + let h1 = PP.create 113 in + let h2 = PP.create 113 in + let _ = LH.fold one_ctx l1 h1 in + let _ = LH.fold one_ctx l2 h2 in + Logs.newline (); + Logs.info "Comparing GlobConstrSys precision of %s (left) with %s (right):" name1 name2; + compare_globals g1 g2; + compare_locals h1 h2; + compare_locals_ctx l1 l2; + Logs.newline (); +end + +module CompareHashtbl (Var: VarType) (Dom: Lattice.S) (VH: Hashtbl.S with type key = Var.t) = +struct + module Var = + struct + include Printable.Std + include Var + let name () = "var" + + let pretty = pretty_trace + include Printable.SimplePretty ( + struct + type nonrec t = t + let pretty = pretty + end + ) + end + + include PrecCompare.MakeHashtbl (Var) (Dom) (VH) +end + +module CompareEqSys (Sys: EqConstrSys) (VH: Hashtbl.S with type key = Sys.Var.t) = +struct + module Compare = CompareHashtbl (Sys.Var) (Sys.Dom) (VH) + + let compare (name1, name2) vh1 vh2 = + Logs.newline (); + Logs.info "Comparing EqConstrSys precision of %s (left) with %s (right):" name1 name2; + let verbose = get_bool "dbg.compare_runs.diff" in + let (_, msg) = Compare.compare ~verbose ~name1 vh1 ~name2 vh2 in + Logs.info "EqConstrSys comparison summary: %t" (fun () -> msg); + Logs.newline (); +end + +module CompareGlobal (GVar: VarType) (G: Lattice.S) (GH: Hashtbl.S with type key = GVar.t) = +struct + module Compare = CompareHashtbl (GVar) (G) (GH) + + let compare (name1, name2) vh1 vh2 = + Logs.newline (); + Logs.info "Comparing globals precision of %s (left) with %s (right):" name1 name2; + let verbose = get_bool "dbg.compare_runs.diff" in + let (_, msg) = Compare.compare ~verbose ~name1 vh1 ~name2 vh2 in + Logs.info "Globals comparison summary: %t" (fun () -> msg); + Logs.newline (); +end + +module CompareNode (C: Printable.S) (D: Lattice.S) (LH: Hashtbl.S with type key = VarF (C).t) = +struct + module Node = + struct + include Node + let var_id _ = "nodes" + let node x = x + let is_write_only _ = false + end + module NH = Hashtbl.Make (Node) + + module Compare = CompareHashtbl (Node) (D) (NH) + + let join_contexts (lh: D.t LH.t): D.t NH.t = + let nh = NH.create 113 in + LH.iter (fun (n, _) d -> + let d' = try D.join (NH.find nh n) d with Not_found -> d in + NH.replace nh n d' + ) lh; + nh + + let compare (name1, name2) vh1 vh2 = + Logs.newline (); + Logs.info "Comparing nodes precision of %s (left) with %s (right):" name1 name2; + let vh1' = join_contexts vh1 in + let vh2' = join_contexts vh2 in + let verbose = get_bool "dbg.compare_runs.diff" in + let (_, msg) = Compare.compare ~verbose ~name1 vh1' ~name2 vh2' in + Logs.info "Nodes comparison summary: %t" (fun () -> msg); + Logs.newline (); +end diff --git a/src/framework/constraints.ml b/src/framework/constraints.ml index e0e36801ed..fb4b5081e8 100644 --- a/src/framework/constraints.ml +++ b/src/framework/constraints.ml @@ -8,596 +8,6 @@ open Analyses open ConstrSys open GobConfig -module M = Messages - - -(** Lifts a [Spec] so that the domain is [Hashcons]d *) -module HashconsLifter (S:Spec) - : Spec with module G = S.G - and module C = S.C -= -struct - module HConsedArg = - struct - (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) - (* see https://github.com/goblint/analyzer/issues/1005 *) - let assume_idempotent = GobConfig.get_string "ana.int.refinement" = "never" - end - module D = Lattice.HConsed (S.D) (HConsedArg) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt x = of_elt (D.unlift x) - end - - let name () = S.name () ^" hashconsed" - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init = S.init - let finalize = S.finalize - - let startstate v = D.lift (S.startstate v) - let exitstate v = D.lift (S.exitstate v) - let morphstate v d = D.lift (S.morphstate v (D.unlift d)) - - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) - } - - let context ctx fd = S.context (conv ctx) fd % D.unlift - let startcontext () = S.startcontext () - - let sync ctx reason = - D.lift @@ S.sync (conv ctx) reason - - let query ctx = - S.query (conv ctx) - - let assign ctx lv e = - D.lift @@ S.assign (conv ctx) lv e - - let vdecl ctx v = - D.lift @@ S.vdecl (conv ctx) v - - let branch ctx e tv = - D.lift @@ S.branch (conv ctx) e tv - - let body ctx f = - D.lift @@ S.body (conv ctx) f - - let return ctx r f = - D.lift @@ S.return (conv ctx) r f - - let asm ctx = - D.lift @@ S.asm (conv ctx) - - let skip ctx = - D.lift @@ S.skip (conv ctx) - - let enter ctx r f args = - List.map (fun (x,y) -> D.lift x, D.lift y) @@ S.enter (conv ctx) r f args - - let special ctx r f args = - D.lift @@ S.special (conv ctx) r f args - - let combine_env ctx r fe f args fc es f_ask = - D.lift @@ S.combine_env (conv ctx) r fe f args fc (D.unlift es) f_ask - - let combine_assign ctx r fe f args fc es f_ask = - D.lift @@ S.combine_assign (conv ctx) r fe f args fc (D.unlift es) f_ask - - let threadenter ctx ~multiple lval f args = - List.map D.lift @@ S.threadenter (conv ctx) ~multiple lval f args - - let threadspawn ctx ~multiple lval f args fctx = - D.lift @@ S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) - - let paths_as_set ctx = - List.map (fun x -> D.lift x) @@ S.paths_as_set (conv ctx) - - let event ctx e octx = - D.lift @@ S.event (conv ctx) e (conv octx) -end - -(** Lifts a [Spec] so that the context is [Hashcons]d. *) -module HashconsContextLifter (S:Spec) - : Spec with module D = S.D - and module G = S.G - and module C = Printable.HConsed (S.C) -= -struct - module D = S.D - module G = S.G - module C = Printable.HConsed (S.C) - module V = S.V - module P = S.P - - let name () = S.name () ^" context hashconsed" - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init = S.init - let finalize = S.finalize - - let startstate = S.startstate - let exitstate = S.exitstate - let morphstate = S.morphstate - - let conv ctx = - { ctx with context = (fun () -> C.unlift (ctx.context ())) } - - let context ctx fd = C.lift % S.context (conv ctx) fd - let startcontext () = C.lift @@ S.startcontext () - - let sync ctx reason = - S.sync (conv ctx) reason - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | Queries.IterPrevVars f -> - let g i (n, c, j) e = f i (n, Obj.repr (C.lift (Obj.obj c)), j) e in - S.query (conv ctx) (Queries.IterPrevVars g) - | _ -> S.query (conv ctx) q - - let assign ctx lv e = - S.assign (conv ctx) lv e - - let vdecl ctx v = - S.vdecl (conv ctx) v - - let branch ctx e tv = - S.branch (conv ctx) e tv - - let body ctx f = - S.body (conv ctx) f - - let return ctx r f = - S.return (conv ctx) r f - - let asm ctx = - S.asm (conv ctx) - - let skip ctx = - S.skip (conv ctx) - - let enter ctx r f args = - S.enter (conv ctx) r f args - - let special ctx r f args = - S.special (conv ctx) r f args - - let combine_env ctx r fe f args fc es f_ask = - S.combine_env (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - - let combine_assign ctx r fe f args fc es f_ask = - S.combine_assign (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask - - let threadenter ctx ~multiple lval f args = - S.threadenter (conv ctx) ~multiple lval f args - - let threadspawn ctx ~multiple lval f args fctx = - S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) - - let paths_as_set ctx = S.paths_as_set (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - -(* see option ana.opt.equal *) -module OptEqual (S: Spec) = struct - module D = struct include S.D let equal x y = x == y || equal x y end - module G = struct include S.G let equal x y = x == y || equal x y end - module C = struct include S.C let equal x y = x == y || equal x y end - include (S : Spec with module D := D and module G := G and module C := C) -end - -(** If dbg.slice.on, stops entering functions after dbg.slice.n levels. *) -module LevelSliceLifter (S:Spec) - : Spec with module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) - and module G = S.G - and module C = S.C -= -struct - module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - let name () = S.name ()^" level sliced" - - let start_level = ref (`Top) - - type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) - let init marshal = - if get_bool "dbg.slice.on" then - start_level := `Lifted (Int64.of_int (get_int "dbg.slice.n")); - S.init marshal - - let finalize = S.finalize - - let startstate v = (S.startstate v, !start_level) - let exitstate v = (S.exitstate v, !start_level) - let morphstate v (d,l) = (S.morphstate v d, l) - - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) - } - - let context ctx fd (d,_) = S.context (conv ctx) fd d - let startcontext () = S.startcontext () - - let lift_fun ctx f g h = - f @@ h (g (conv ctx)) - - let enter' ctx r f args = - let liftmap = List.map (fun (x,y) -> (x, snd ctx.local), (y, snd ctx.local)) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) - - let lift ctx d = (d, snd ctx.local) - let lift_start_level d = (d, !start_level) - - let sync ctx reason = lift_fun ctx (lift ctx) S.sync ((|>) reason) - let query' ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun x -> x q) - let assign ctx lv e = lift_fun ctx (lift ctx) S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx (lift ctx) S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx (lift ctx) S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx (lift ctx) S.body ((|>) f) - let return ctx r f = lift_fun ctx (lift ctx) S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx (lift ctx) S.asm identity - let skip ctx = lift_fun ctx (lift ctx) S.skip identity - let special ctx r f args = lift_fun ctx (lift ctx) S.special ((|>) args % (|>) f % (|>) r) - let combine_env' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (lift ctx) (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - - let leq0 = function - | `Top -> false - | `Lifted x -> x <= 0L - | `Bot -> true - - let sub1 = function - | `Lifted x -> `Lifted (Int64.sub x 1L) - | x -> x - - let add1 = function - | `Lifted x -> `Lifted (Int64.add x 1L) - | x -> x - - let paths_as_set ctx = - let liftmap = List.map (fun x -> (x, snd ctx.local)) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) - - let event ctx e octx = - lift_fun ctx (lift ctx) S.event ((|>) (conv octx) % (|>) e) - - let enter ctx r f args = - let (d,l) = ctx.local in - if leq0 l then - [ctx.local, D.bot ()] - else - enter' {ctx with local=(d, sub1 l)} r f args - - let combine_env ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in - let l = add1 l in - if leq0 l then - (d, l) - else - let d',_ = combine_env' ctx r fe f args fc es f_ask in - (d', l) - - let combine_assign ctx r fe f args fc es f_ask = - let (d,l) = ctx.local in - (* No need to add1 here, already done in combine_env. *) - if leq0 l then - (d, l) - else - let d',_ = combine_assign' ctx r fe f args fc es f_ask in - (d', l) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | Queries.EvalFunvar e -> - let (d,l) = ctx.local in - if leq0 l then - Queries.AD.empty () - else - query' ctx (Queries.EvalFunvar e) - | q -> query' ctx q -end - - -(** Limits the number of widenings per node. *) -module LimitLifter (S:Spec) = -struct - include (S : module type of S with module D := S.D and type marshal = S.marshal) - - let name () = S.name ()^" limited" - - let limit = ref 0 - - let init marshal = - limit := get_int "dbg.limit.widen"; - S.init marshal - - module H = MyCFG.NodeH - let h = H.create 13 - let incr k = - H.modify_def 1 k (fun v -> - if v >= !limit then failwith (GobPretty.sprintf "LimitLifter: Reached limit (%d) for node %a" !limit Node.pretty_plain_short (Option.get !MyCFG.current_node)); - v+1 - ) h; - module D = struct - include S.D - let widen x y = Option.may incr !MyCFG.current_node; widen x y (* when is this None? *) - end -end - - -(* widening on contexts, keeps contexts for calls only in D *) -module WidenContextLifterSide (S:Spec) -= -struct - module DD = - struct - include S.D - let printXml f d = BatPrintf.fprintf f "%a" printXml d - end - module M = MapDomain.MapBot (Basetype.Variables) (DD) (* should be CilFun -> S.C, but CilFun is not Groupable, and S.C is no Lattice *) - - module D = struct - include Lattice.Prod (S.D) (M) - let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.D.printXml d M.printXml m - end - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - - let name () = S.name ()^" with widened contexts" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - let inj f x = f x, M.bot () - - let startcontext () = S.startcontext () - let startstate = inj S.startstate - let exitstate = inj S.exitstate - let morphstate v (d,m) = S.morphstate v d, m - - - let conv ctx = - { ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) - } - - let context ctx fd (d,m) = S.context (conv ctx) fd d (* just the child analysis' context *) - - let lift_fun ctx f g = g (f (conv ctx)), snd ctx.local - - let sync ctx reason = lift_fun ctx S.sync ((|>) reason) - let query ctx = S.query (conv ctx) - let assign ctx lv e = lift_fun ctx S.assign ((|>) e % (|>) lv) - let vdecl ctx v = lift_fun ctx S.vdecl ((|>) v) - let branch ctx e tv = lift_fun ctx S.branch ((|>) tv % (|>) e) - let body ctx f = lift_fun ctx S.body ((|>) f) - let return ctx r f = lift_fun ctx S.return ((|>) f % (|>) r) - let asm ctx = lift_fun ctx S.asm identity - let skip ctx = lift_fun ctx S.skip identity - let special ctx r f args = lift_fun ctx S.special ((|>) args % (|>) f % (|>) r) - - let event ctx e octx = lift_fun ctx S.event ((|>) (conv octx) % (|>) e) - - let threadenter ctx ~multiple lval f args = S.threadenter (conv ctx) ~multiple lval f args |> List.map (fun d -> (d, snd ctx.local)) - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) - - let enter ctx r f args = - let m = snd ctx.local in - let d' v_cur = - if ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.context.widen" ~keepAttr:"widen" ~removeAttr:"no-widen" f then ( - let v_old = M.find f.svar m in (* S.D.bot () if not found *) - let v_new = S.D.widen v_old (S.D.join v_old v_cur) in - Messages.(if tracing && not (S.D.equal v_old v_new) then tracel "widen-context" "enter results in new context for function %s" f.svar.vname); - v_new, M.add f.svar v_new m - ) - else - v_cur, m - in - S.enter (conv ctx) r f args - |> List.map (fun (c,v) -> (c,m), d' v) (* c: caller, v: callee *) - - let paths_as_set ctx = - let m = snd ctx.local in - S.paths_as_set (conv ctx) |> List.map (fun v -> (v,m)) - - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) -end - - -(** Lifts a [Spec] with a special bottom element that represent unreachable code. *) -module DeadCodeLifter (S:Spec) - : Spec with module D = Dom (S.D) - and module G = S.G - and module C = S.C -= -struct - module D = Dom (S.D) - module G = S.G - module C = S.C - module V = S.V - module P = - struct - include Printable.Option (S.P) (struct let name = "None" end) - - let of_elt = function - | `Lifted x -> Some (S.P.of_elt x) - | _ -> None - end - - let name () = S.name ()^" lifted" - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - - let startcontext () = S.startcontext () - let startstate v = `Lifted (S.startstate v) - let exitstate v = `Lifted (S.exitstate v) - let morphstate v d = try `Lifted (S.morphstate v (D.unlift d)) with Deadcode -> d - - - let conv ctx = - { ctx with local = D.unlift ctx.local - ; split = (fun d es -> ctx.split (D.lift d) es ) - } - - let context ctx fd = S.context (conv ctx) fd % D.unlift - - let lift_fun ctx f g h b = - try f @@ h (g (conv ctx)) - with Deadcode -> b - - let sync ctx reason = lift_fun ctx D.lift S.sync ((|>) reason) `Bot - - let enter ctx r f args = - let liftmap = List.map (fun (x,y) -> D.lift x, D.lift y) in - lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) [] - - let paths_as_set ctx = - let liftmap = List.map (fun x -> D.lift x) in - lift_fun ctx liftmap S.paths_as_set (Fun.id) [D.bot ()] (* One dead path instead of none, such that combine_env gets called for functions with dead normal return (and thus longjmpy returns can be correctly handled by lifter). *) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - lift_fun ctx identity S.query (fun (x) -> x q) (Queries.Result.bot q) - let assign ctx lv e = lift_fun ctx D.lift S.assign ((|>) e % (|>) lv) `Bot - let vdecl ctx v = lift_fun ctx D.lift S.vdecl ((|>) v) `Bot - let branch ctx e tv = lift_fun ctx D.lift S.branch ((|>) tv % (|>) e) `Bot - let body ctx f = lift_fun ctx D.lift S.body ((|>) f) `Bot - let return ctx r f = lift_fun ctx D.lift S.return ((|>) f % (|>) r) `Bot - let asm ctx = lift_fun ctx D.lift S.asm identity `Bot - let skip ctx = lift_fun ctx D.lift S.skip identity `Bot - let special ctx r f args = lift_fun ctx D.lift S.special ((|>) args % (|>) f % (|>) r) `Bot - let combine_env ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot - - let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] - let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx D.lift (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot - - let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot -end - -module NoContext = struct let name = "no context" end -module IntConf = -struct - let n () = max_int - let names x = Format.asprintf "%d" x -end - -(** Lifts a [Spec] with the context gas variable. The gas variable limits the number of context-sensitively analyzed function calls in a call stack. - For every function call the gas is reduced. If the gas is zero, the remaining function calls are analyzed without context-information *) -module ContextGasLifter (S:Spec) - : Spec with module D = Lattice.Prod (S.D) (Lattice.Chain (IntConf)) - and module C = Printable.Option (S.C) (NoContext) - and module G = S.G -= -struct - include S - - module Context_Gas_Prod (Base1: Lattice.S) (Base2: Lattice.S) = - struct - include Lattice.Prod (Base1) (Base2) - let printXml f (x,y) = - BatPrintf.fprintf f "\n%a\n%a\n" Base1.printXml x Base2.printXml y - end - module D = Context_Gas_Prod (S.D) (Lattice.Chain (IntConf)) (* Product of S.D and an integer, tracking the context gas value *) - module C = Printable.Option (S.C) (NoContext) - module G = S.G - module V = S.V - module P = - struct - include S.P - let of_elt (x, _) = of_elt x - end - - (* returns context gas value of the given ctx *) - let cg_val ctx = snd ctx.local - - type marshal = S.marshal - let init = S.init - let finalize = S.finalize - - - let startcontext () = Some (S.startcontext ()) - let name () = S.name ()^" with context gas" - let startstate v = S.startstate v, get_int "ana.context.gas_value" - let exitstate v = S.exitstate v, get_int "ana.context.gas_value" - let morphstate v (d,i) = S.morphstate v d, i - - let conv (ctx:(D.t,G.t,C.t,V.t) ctx): (S.D.t,G.t,S.C.t,V.t)ctx = - if (cg_val ctx <= 0) - then {ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, cg_val ctx) es) - ; context = (fun () -> ctx_failwith "no context (contextGas = 0)")} - else {ctx with local = fst ctx.local - ; split = (fun d es -> ctx.split (d, cg_val ctx) es) - ; context = (fun () -> Option.get (ctx.context ()))} - - let context ctx fd (d,i) = - (* only keep context if the context gas is greater zero *) - if i <= 0 then None else Some (S.context (conv ctx) fd d) - - let enter ctx r f args = - (* callee gas = caller gas - 1 *) - let liftmap_tup = List.map (fun (x,y) -> (x, cg_val ctx), (y, max 0 (cg_val ctx - 1))) in - liftmap_tup (S.enter (conv ctx) r f args) - - let threadenter ctx ~multiple lval f args = - let liftmap f = List.map (fun (x) -> (x, max 0 (cg_val ctx - 1))) f in - liftmap (S.threadenter (conv ctx) ~multiple lval f args) - - let query ctx (type a) (q: a Queries.t):a Queries.result = - match q with - | Queries.GasExhausted -> - let (d,i) = ctx.local in - (i <= 0) - | _ -> S.query (conv ctx) q - - let sync ctx reason = S.sync (conv ctx) reason, cg_val ctx - let assign ctx lval expr = S.assign (conv ctx) lval expr, cg_val ctx - let vdecl ctx v = S.vdecl (conv ctx) v, cg_val ctx - let body ctx fundec = S.body (conv ctx) fundec, cg_val ctx - let branch ctx e tv = S.branch (conv ctx) e tv, cg_val ctx - let return ctx r f = S.return (conv ctx) r f, cg_val ctx - let asm ctx = S.asm (conv ctx), cg_val ctx - let skip ctx = S.skip (conv ctx), cg_val ctx - let special ctx r f args = S.special (conv ctx) r f args, cg_val ctx - let combine_env ctx r fe f args fc es f_ask = S.combine_env (conv ctx) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val ctx - let combine_assign ctx r fe f args fc es f_ask = S.combine_assign (conv ctx) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val ctx - let paths_as_set ctx = List.map (fun (x) -> (x, cg_val ctx)) @@ S.paths_as_set (conv ctx) - let threadspawn ctx ~multiple lval f args fctx = S.threadspawn (conv ctx) ~multiple lval f args (conv fctx), cg_val ctx - let event ctx e octx = S.event (conv ctx) e (conv octx), cg_val ctx -end - module type Increment = sig @@ -632,8 +42,8 @@ struct match ctx.prev_node, Cfg.prev ctx.prev_node with | _, _ :: _ :: _ -> (* Join in CFG. *) S.sync ctx `Join - | FunctionEntry _, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) - S.sync ctx `JoinCall + | FunctionEntry f, _ -> (* Function entry, also needs sync because partial contexts joined by solver, see 00-sanity/35-join-contexts. *) + S.sync ctx (`JoinCall f) | _, _ -> S.sync ctx `Normal let side_context sideg f c = @@ -713,11 +123,13 @@ struct let tf_assign var edge prev_node lv e getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.assign ctx lv e) !r !spawns + let d = S.assign ctx lv e in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let tf_vdecl var edge prev_node v getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.vdecl ctx v) !r !spawns + let d = S.vdecl ctx v in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let normal_return r fd ctx sideg = let spawning_return = S.return ctx r fd in @@ -732,7 +144,7 @@ struct let tf_ret var edge prev_node ret fd getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - let d = + let d = (* Force transfer function to be evaluated before dereferencing in common_join argument. *) if (CilType.Fundec.equal fd MyCFG.dummy_func || List.mem fd.svar.vname (get_string_list "mainfun")) && get_bool "kernel" @@ -747,11 +159,13 @@ struct let c: unit -> S.C.t = snd var |> Obj.obj in side_context sideg fd (c ()); let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.body ctx fd) !r !spawns + let d = S.body ctx fd in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let tf_test var edge prev_node e tv getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.branch ctx e tv) !r !spawns + let d = S.branch ctx e tv in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let tf_normal_call ctx lv e (f:fundec) args getl sidel getg sideg = let combine (cd, fc, fd) = @@ -870,11 +284,13 @@ struct let tf_asm var edge prev_node getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.asm ctx) !r !spawns + let d = S.asm ctx in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let tf_skip var edge prev_node getl sidel getg sideg d = let ctx, r, spawns = common_ctx var edge prev_node d getl sidel getg sideg in - common_join ctx (S.skip ctx) !r !spawns + let d = S.skip ctx in (* Force transfer function to be evaluated before dereferencing in common_join argument. *) + common_join ctx d !r !spawns let tf var getl sidel getg sideg prev_node edge d = begin match edge with @@ -1124,886 +540,3 @@ struct {obsolete; delete; reluctant; restart} end - - -(** Add path sensitivity to a analysis *) -module PathSensitive2 (Spec:Spec) - : Spec - with module G = Spec.G - and module C = Spec.C - and module V = Spec.V -= -struct - module D = - struct - (* TODO is it really worth it to check every time instead of just using sets and joining later? *) - module R = - struct - include Spec.P - type elt = Spec.D.t - end - module J = SetDomain.Joined (Spec.D) - include DisjointDomain.ProjectiveSet (Spec.D) (J) (R) - let name () = "PathSensitive (" ^ name () ^ ")" - - let printXml f x = - let print_one x = - BatPrintf.fprintf f "\n%a" Spec.D.printXml x - in - iter print_one x - end - - module G = Spec.G - module C = Spec.C - module V = Spec.V - module P = UnitP - - let name () = "PathSensitive2("^Spec.name ()^")" - - type marshal = Spec.marshal - let init = Spec.init - let finalize = Spec.finalize - - let startcontext () = Spec.startcontext () - let exitstate v = D.singleton (Spec.exitstate v) - let startstate v = D.singleton (Spec.startstate v) - let morphstate v d = D.map (Spec.morphstate v) d - - let conv ctx x = - let rec ctx' = { ctx with ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx' q) - ; local = x - ; split = (ctx.split % D.singleton) } - in - ctx' - - let context ctx fd l = - if D.cardinal l <> 1 then - failwith "PathSensitive2.context must be called with a singleton set." - else - let x = D.choose l in - Spec.context (conv ctx x) fd x - - - let map ctx f g = - let h x xs = - try D.add (g (f (conv ctx x))) xs - with Deadcode -> xs - in - let d = D.fold h ctx.local (D.empty ()) in - if D.is_bot d then raise Deadcode else d - - let fold' ctx f g h a = - let k x a = - try h a @@ g @@ f @@ conv ctx x - with Deadcode -> a - in - D.fold k ctx.local a - - let assign ctx l e = map ctx Spec.assign (fun h -> h l e ) - let vdecl ctx v = map ctx Spec.vdecl (fun h -> h v) - let body ctx f = map ctx Spec.body (fun h -> h f ) - let return ctx e f = map ctx Spec.return (fun h -> h e f ) - let branch ctx e tv = map ctx Spec.branch (fun h -> h e tv) - let asm ctx = map ctx Spec.asm identity - let skip ctx = map ctx Spec.skip identity - let special ctx l f a = map ctx Spec.special (fun h -> h l f a) - - let event ctx e octx = - let fd1 = D.choose octx.local in - map ctx Spec.event (fun h -> h e (conv octx fd1)) - - let threadenter ctx ~multiple lval f args = - let g xs ys = (List.map (fun y -> D.singleton y) ys) @ xs in - fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] - - let threadspawn ctx ~multiple lval f args fctx = - let fd1 = D.choose fctx.local in - map ctx (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fctx fd1)) - - let sync ctx reason = map ctx Spec.sync (fun h -> h reason) - - let query ctx (type a) (q: a Queries.t): a Queries.result = - (* TODO: handle Invariant path like PathSensitive3? *) - (* join results so that they are sound for all paths *) - let module Result = (val Queries.Result.lattice q) in - fold' ctx Spec.query identity (fun x f -> Result.join x (f q)) (Result.bot ()) - - let enter ctx l f a = - let g xs ys = (List.map (fun (x,y) -> D.singleton x, D.singleton y) ys) @ xs in - fold' ctx Spec.enter (fun h -> h l f a) g [] - - let paths_as_set ctx = - (* Path-sensitivity is only here, not below! *) - let elems = D.elements ctx.local in - List.map (D.singleton) elems - - let combine_env ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in - let k x y = - if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; - try - let r = Spec.combine_env (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; - D.add r y - with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead"; - y - in - let d = D.fold k d (D.bot ()) in - if D.is_bot d then raise Deadcode else d - - let combine_assign ctx l fe f a fc d f_ask = - assert (D.cardinal ctx.local = 1); - let cd = D.choose ctx.local in - let k x y = - if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; - try - let r = Spec.combine_assign (conv ctx cd) l fe f a fc x f_ask in - if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; - D.add r y - with Deadcode -> - if M.tracing then M.traceu "combine" "combined function: dead"; - y - in - let d = D.fold k d (D.bot ()) in - if D.is_bot d then raise Deadcode else d -end - -module DeadBranchLifter (S: Spec): Spec = -struct - include S - - let name () = "DeadBranch (" ^ S.name () ^ ")" - - (* Two global invariants: - 1. S.V -> S.G -- used for S - 2. node -> (exp -> flat bool) -- used for warnings *) - - module V = - struct - include Printable.EitherConf (struct let expand1 = false let expand2 = true end) (S.V) (Node) - let name () = "DeadBranch" - let s x = `Left x - let node x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | `Right _ -> true - end - - module EM = - struct - include MapDomain.MapBot (Basetype.CilExp) (BoolDomain.FlatBool) - let name () = "branches" - end - - module G = - struct - include Lattice.Lift2 (S.G) (EM) - let name () = "deadbranch" - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "DeadBranchLifter.s" - let node = function - | `Bot -> EM.bot () - | `Lifted2 x -> x - | _ -> failwith "DeadBranchLifter.node" - let create_s s = `Lifted1 s - let create_node node = `Lifted2 node - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | `Right g -> - let em = G.node (ctx.global (V.node g)) in - EM.iter (fun exp tv -> - match tv with - | `Lifted tv -> - let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) - let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in - M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv - | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) - M.msg_final Error ~category:Analyzer ~tags:[Category Unsound] "Both branches dead"; - M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp - | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) - | `Top -> (* may be both true and false *) - () - ) em; - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | `Right g -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - - (* node vars for dead branches *) - begin match vq with - | Node {node; _} -> - vf (Obj.repr (V.node node)) - | _ -> - () - end - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - let context ctx = S.context (conv ctx) - - let branch ctx exp tv = - if !AnalysisState.postsolving then ( - try - let r = branch ctx exp tv in - (* branch is live *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) - r - with Deadcode -> - (* branch is dead *) - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) - raise Deadcode - ) - else ( - ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) - branch ctx exp tv - ) - - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx = S.combine_env (conv ctx) - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - -module LongjmpLifter (S: Spec): Spec = -struct - include S - - let name () = "Longjmp (" ^ S.name () ^ ")" - - module V = - struct - include Printable.Either3Conf (struct let expand1 = false let expand2 = true let expand3 = true end) (S.V) (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C)) - let name () = "longjmp" - let s x = `Left x - let longjmpto x = `Middle x - let longjmpret x = `Right x - let is_write_only = function - | `Left x -> S.V.is_write_only x - | _ -> false - end - - module G = - struct - include Lattice.Lift2 (S.G) (S.D) - - let s = function - | `Bot -> S.G.bot () - | `Lifted1 x -> x - | _ -> failwith "LongjmpLifter.s" - let local = function - | `Bot -> S.D.bot () - | `Lifted2 x -> x - | _ -> failwith "LongjmpLifter.local" - let create_s s = `Lifted1 s - let create_local local = `Lifted2 local - - let printXml f = function - | `Lifted1 x -> S.G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" S.D.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - end - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.s (ctx.global (V.s v))); - sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); - } - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (WarnGlobal (Obj.repr g)) - | _ -> - Queries.Result.top q - end - | InvariantGlobal g -> - let g: V.t = Obj.obj g in - begin match g with - | `Left g -> - S.query (conv ctx) (InvariantGlobal (Obj.repr g)) - | _ -> - Queries.Result.top q - end - | IterSysVars (vq, vf) -> - (* vars for S *) - let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in - S.query (conv ctx) (IterSysVars (vq, vf')); - (* TODO: vars? *) - | _ -> - S.query (conv ctx) q - - - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - let enter ctx = S.enter (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let context ctx = S.context (conv ctx) - - let combine_env ctx lv e f args fc fd f_ask = - let conv_ctx = conv ctx in - let current_fundec = Node.find_fundec ctx.node in - let handle_longjmp (cd, fc, longfd) = - (* This is called per-path. *) - let rec cd_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); - local = cd; - } - in - let longfd_ctx = - (* Inner scope to prevent unsynced longfd_ctx from being used. *) - (* Extra sync like with normal combine. *) - let rec sync_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query sync_ctx q); - local = longfd; - prev_node = Function f; - } - in - let synced = S.sync sync_ctx `Join in - let rec longfd_ctx = - { sync_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query longfd_ctx q); - local = synced; - } - in - longfd_ctx - in - let combined = lazy ( (* does not depend on target, do at most once *) - (* Globals are non-problematic here, as they are always carried around without any issues! *) - (* A combine call is mostly needed to ensure locals have appropriate values. *) - (* Using f from called function on purpose here! Needed? *) - S.combine_env cd_ctx None e f args fc longfd_ctx.local (Analyses.ask_of_ctx longfd_ctx) (* no lval because longjmp return skips return value assignment *) - ) - in - let returned = lazy ( (* does not depend on target, do at most once *) - let rec combined_ctx = - { cd_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query combined_ctx q); - local = Lazy.force combined; - } - in - S.return combined_ctx None current_fundec - ) - in - let (active_targets, _) = longfd_ctx.ask ActiveJumpBuf in - let valid_targets = cd_ctx.ask ValidLongJmp in - let handle_target target = match target with - | JmpBufDomain.BufferEntryOrTop.AllTargets -> () (* The warning is already emitted at the point where the longjmp happens *) - | Target (target_node, target_context) -> - let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Fun: Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force combined)) - (* No need to propagate this outwards here, the set of valid longjumps is part of the context, we can never have the same context setting the longjmp multiple times *) - ) - (* Appropriate setjmp is not in current function & current context *) - else if JmpBufDomain.JmpBufSet.mem target valid_targets then - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) - else - (* It actually is not handled here but was propagated here spuriously, we already warned at the location where this issue is caused *) - (* As the validlongjumps inside the callee is a a superset of the ones inside the caller *) - () - in - JmpBufDomain.JmpBufSet.iter handle_target active_targets - in - if M.tracing then M.tracel "longjmp" "longfd getg %a" CilType.Fundec.pretty f; - let longfd = G.local (ctx.global (V.longjmpret (f, Option.get fc))) in - if M.tracing then M.tracel "longjmp" "longfd %a" D.pretty longfd; - if not (D.is_bot longfd) then - handle_longjmp (ctx.local, fc, longfd); - S.combine_env (conv_ctx) lv e f args fc fd f_ask - - let combine_assign ctx lv e f args fc fd f_ask = - S.combine_assign (conv ctx) lv e f args fc fd f_ask - - let special ctx lv f args = - let conv_ctx = conv ctx in - match (LibraryFunctions.find f).special args with - | Setjmp {env} -> - (* Handling of returning for the first time *) - let normal_return = S.special conv_ctx lv f args in - let jmp_return = G.local (ctx.global (V.longjmpto (ctx.prev_node, ctx.context ()))) in - if S.D.is_bot jmp_return then - normal_return - else ( - let rec jmp_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query jmp_ctx q); - local = jmp_return; - } - in - let longjmped = S.event jmp_ctx (Events.Longjmped {lval=lv}) jmp_ctx in - S.D.join normal_return longjmped - ) - | Longjmp {env; value} -> - let current_fundec = Node.find_fundec ctx.node in - let handle_path path = ( - let rec path_ctx = - { conv_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query path_ctx q); - local = path; - } - in - let specialed = lazy ( (* does not depend on target, do at most once *) - S.special path_ctx lv f args - ) - in - let returned = lazy ( (* does not depend on target, do at most once *) - let rec specialed_ctx = - { path_ctx with - ask = (fun (type a) (q: a Queries.t) -> S.query specialed_ctx q); - local = Lazy.force specialed; - } - in - S.return specialed_ctx None current_fundec - ) - in - (* Eval `env` again to avoid having to construct bespoke ctx to ask *) - let targets = path_ctx.ask (EvalJumpBuf env) in - let valid_targets = path_ctx.ask ValidLongJmp in - if M.tracing then Messages.tracel "longjmp" "Jumping to %a" JmpBufDomain.JmpBufSet.pretty targets; - let handle_target target = match target with - | JmpBufDomain.BufferEntryOrTop.AllTargets -> - M.warn ~category:Imprecise "Longjmp to potentially invalid target, as contents of buffer %a may be unknown! (imprecision due to heap?)" d_exp env; - M.msg_final Error ~category:Unsound ~tags:[Category Imprecise; Category Call] "Longjmp to unknown target ignored" - | Target (target_node, target_context) -> - let target_fundec = Node.find_fundec target_node in - if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( - if M.tracing then Messages.tracel "longjmp" "Potentially from same context, side-effect to %a" Node.pretty target_node; - ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force specialed)) - ) - else if JmpBufDomain.JmpBufSet.mem target valid_targets then ( - if M.tracing then Messages.tracel "longjmp" "Longjmp to somewhere else, side-effect to %i" (S.C.hash (ctx.context ())); - ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) - ) - else - M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target! (Target %a in Function %a which may have already returned or is in a different thread)" Node.pretty target_node CilType.Fundec.pretty target_fundec - in - if JmpBufDomain.JmpBufSet.is_empty targets then - M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target (%a is bot?!)" d_exp env - else - JmpBufDomain.JmpBufSet.iter handle_target targets - ) - in - List.iter handle_path (S.paths_as_set conv_ctx); - if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( - AnalysisState.svcomp_may_not_terminate := true; - M.warn ~category:Termination "The program might not terminate! (Longjmp)" - ); - S.D.bot () - | _ -> S.special conv_ctx lv f args - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - - -(** Add cycle detection in the context-sensitive dynamic function call graph to an analysis *) -module RecursionTermLifter (S: Spec) - : Spec with module D = S.D - and module C = S.C -= -(* two global invariants: - - S.V -> S.G - Needed to store the previously built global invariants - - fundec * S.C -> (Set (fundec * S.C)) - The second global invariant maps from the callee fundec and context to a set of caller fundecs and contexts. - This structure therefore stores the context-sensitive call graph. - For example: - let the function f in context c call function g in context c'. - In the global invariant structure it would be stored like this: (g,c') -> {(f, c)} -*) - -struct - include S - - (* contains all the callee fundecs and contexts *) - module V = GVarFC(S.V)(S.C) - - (* Tuple containing the fundec and context of a caller *) - module Call = Printable.Prod (CilType.Fundec) (S.C) - - (* Set containing multiple caller tuples *) - module CallerSet = SetDomain.Make (Call) - - module G = - struct - include Lattice.Lift2 (G) (CallerSet) - - let spec = function - | `Bot -> G.bot () - | `Lifted1 x -> x - | _ -> failwith "RecursionTermLifter.spec" - - let callers = function - | `Bot -> CallerSet.bot () - | `Lifted2 x -> x - | _ -> failwith "RecursionTermLifter.callGraph" - - let create_spec spec = `Lifted1 spec - let create_singleton_caller caller = `Lifted2 (CallerSet.singleton caller) - - let printXml f = function - | `Lifted1 x -> G.printXml f x - | `Lifted2 x -> BatPrintf.fprintf f "%a" CallerSet.printXml x - | x -> BatPrintf.fprintf f "%a" printXml x - - end - - let name () = "RecursionTermLifter (" ^ S.name () ^ ")" - - let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = - { ctx with - global = (fun v -> G.spec (ctx.global (V.spec v))); - sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_spec g)); - } - - let cycleDetection ctx call = - let module LH = Hashtbl.Make (Printable.Prod (CilType.Fundec) (S.C)) in - let module LS = Set.Make (Printable.Prod (CilType.Fundec) (S.C)) in - (* find all cycles/SCCs *) - let global_visited_calls = LH.create 100 in - - (* DFS *) - let rec iter_call (path_visited_calls: LS.t) ((fundec, _) as call) = - if LS.mem call path_visited_calls then ( - AnalysisState.svcomp_may_not_terminate := true; (*set the indicator for a non-terminating program for the sv comp*) - (*Cycle found*) - let loc = M.Location.CilLocation fundec.svar.vdecl in - M.warn ~loc ~category:Termination "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec) (* output a warning for non-termination*) - else if not (LH.mem global_visited_calls call) then begin - LH.replace global_visited_calls call (); - let new_path_visited_calls = LS.add call path_visited_calls in - let gvar = V.call call in - let callers = G.callers (ctx.global gvar) in - CallerSet.iter (fun to_call -> - iter_call new_path_visited_calls to_call - ) callers; - end - in - iter_call LS.empty call - - let query ctx (type a) (q: a Queries.t): a Queries.result = - match q with - | WarnGlobal v -> - (* check result of loop analysis *) - if not (ctx.ask Queries.MustTermAllLoops) then - AnalysisState.svcomp_may_not_terminate := true; - let v: V.t = Obj.obj v in - begin match v with - | `Left v' -> - S.query (conv ctx) (WarnGlobal (Obj.repr v')) - | `Right call -> cycleDetection ctx call (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) - end - | InvariantGlobal v -> - let v: V.t = Obj.obj v in - begin match v with - | `Left v -> - S.query (conv ctx) (InvariantGlobal (Obj.repr v)) - | `Right v -> - Queries.Result.top q - end - | _ -> S.query (conv ctx) q - - let branch ctx = S.branch (conv ctx) - let assign ctx = S.assign (conv ctx) - let vdecl ctx = S.vdecl (conv ctx) - - - let record_call sideg callee caller = - sideg (V.call callee) (G.create_singleton_caller caller) - - let enter ctx = S.enter (conv ctx) - let context ctx = S.context (conv ctx) - let paths_as_set ctx = S.paths_as_set (conv ctx) - let body ctx = S.body (conv ctx) - let return ctx = S.return (conv ctx) - let combine_env ctx r fe f args fc es f_ask = - if !AnalysisState.postsolving then ( - let c_r: S.C.t = ctx.context () in (* Caller context *) - let nodeF = ctx.node in - let fd_r : fundec = Node.find_fundec nodeF in (* Caller fundec *) - let caller: (fundec * S.C.t) = (fd_r, c_r) in - let c_e: S.C.t = Option.get fc in (* Callee context *) - let fd_e : fundec = f in (* Callee fundec *) - let callee = (fd_e, c_e) in - record_call ctx.sideg callee caller - ); - S.combine_env (conv ctx) r fe f args fc es f_ask - - let combine_assign ctx = S.combine_assign (conv ctx) - let special ctx = S.special (conv ctx) - let threadenter ctx = S.threadenter (conv ctx) - let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) - let sync ctx = S.sync (conv ctx) - let skip ctx = S.skip (conv ctx) - let asm ctx = S.asm (conv ctx) - let event ctx e octx = S.event (conv ctx) e (conv octx) -end - -module CompareGlobSys (SpecSys: SpecSys) = -struct - open SpecSys - module Sys = EQSys - module LH = LHT - module GH = GHT - - open Spec - module G = Sys.G - - module PP = Hashtbl.Make (Node) - - let compare_globals g1 g2 = - let eq, le, gr, uk = ref 0, ref 0, ref 0, ref 0 in - let f_eq () = incr eq in - let f_le () = incr le in - let f_gr () = incr gr in - let f_uk () = incr uk in - let f k v1 = - let v2 = try GH.find g2 k with Not_found -> G.bot () in - let b1 = G.leq v1 v2 in - let b2 = G.leq v2 v1 in - if b1 && b2 then - f_eq () - else if b1 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "Global %a is more precise using left:\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v2,v1); - f_le () - end else if b2 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "Global %a is more precise using right:\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v1,v2); - f_gr () - end else begin - if get_bool "dbg.compare_runs.diff" then ( - Logs.info "Global %a is incomparable (diff):\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v1,v2); - Logs.info "Global %a is incomparable (reverse diff):\n%a" Sys.GVar.pretty_trace k G.pretty_diff (v2,v1); - ); - f_uk () - end - in - GH.iter f g1; - Logs.info "globals:\tequal = %d\tleft = %d\tright = %d\tincomparable = %d" !eq !le !gr !uk - - let compare_locals h1 h2 = - let eq, le, gr, uk = ref 0, ref 0, ref 0, ref 0 in - let f k v1 = - if PP.mem h2 k then - let v2 = PP.find h2 k in - let b1 = D.leq v1 v2 in - let b2 = D.leq v2 v1 in - if b1 && b2 then - incr eq - else if b1 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "%a @@ %a is more precise using left:\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v2,v1); - incr le - end else if b2 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "%a @@ %a is more precise using right:\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v1,v2); - incr gr - end else begin - if get_bool "dbg.compare_runs.diff" then ( - Logs.info "%a @@ %a is incomparable (diff):\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v1,v2); - Logs.info "%a @@ %a is incomparable (reverse diff):\n%a" Node.pretty_plain k CilType.Location.pretty (Node.location k) D.pretty_diff (v2,v1); - ); - incr uk - end - in - PP.iter f h1; - (* let k1 = Set.of_enum @@ PP.keys h1 in - let k2 = Set.of_enum @@ PP.keys h2 in - let o1 = Set.cardinal @@ Set.diff k1 k2 in - let o2 = Set.cardinal @@ Set.diff k2 k1 in - Logs.info "locals: \tequal = %d\tleft = %d[%d]\tright = %d[%d]\tincomparable = %d" !eq !le o1 !gr o2 !uk *) - Logs.info "locals: \tequal = %d\tleft = %d\tright = %d\tincomparable = %d" !eq !le !gr !uk - - let compare_locals_ctx h1 h2 = - let eq, le, gr, uk, no2, no1 = ref 0, ref 0, ref 0, ref 0, ref 0, ref 0 in - let f_eq () = incr eq in - let f_le () = incr le in - let f_gr () = incr gr in - let f_uk () = incr uk in - let f k v1 = - if not (LH.mem h2 k) then incr no2 else - let v2 = LH.find h2 k in - let b1 = D.leq v1 v2 in - let b2 = D.leq v2 v1 in - if b1 && b2 then - f_eq () - else if b1 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "%a is more precise using left:\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v2,v1); - f_le () - end else if b2 then begin - if get_bool "dbg.compare_runs.diff" then - Logs.info "%a is more precise using right:\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v1,v2); - f_gr () - end else begin - if get_bool "dbg.compare_runs.diff" then ( - Logs.info "%a is incomparable (diff):\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v1,v2); - Logs.info "%a is incomparable (reverse diff):\n%a" Sys.LVar.pretty_trace k D.pretty_diff (v2,v1); - ); - f_uk () - end - in - LH.iter f h1; - let f k v2 = - if not (LH.mem h1 k) then incr no1 - in - LH.iter f h2; - (* let k1 = Set.of_enum @@ PP.keys h1 in *) - (* let k2 = Set.of_enum @@ PP.keys h2 in *) - (* let o1 = Set.cardinal @@ Set.diff k1 k2 in *) - (* let o2 = Set.cardinal @@ Set.diff k2 k1 in *) - Logs.info "locals_ctx:\tequal = %d\tleft = %d\tright = %d\tincomparable = %d\tno_ctx_in_right = %d\tno_ctx_in_left = %d" !eq !le !gr !uk !no2 !no1 - - let compare (name1,name2) (l1,g1) (l2,g2) = - let one_ctx (n,_) v h = - PP.replace h n (try D.join v (PP.find h n) with Not_found -> v); - h - in - (* these contain results where the contexts per node have been joined *) - let h1 = PP.create 113 in - let h2 = PP.create 113 in - let _ = LH.fold one_ctx l1 h1 in - let _ = LH.fold one_ctx l2 h2 in - Logs.newline (); - Logs.info "Comparing GlobConstrSys precision of %s (left) with %s (right):" name1 name2; - compare_globals g1 g2; - compare_locals h1 h2; - compare_locals_ctx l1 l2; - Logs.newline (); -end - -module CompareHashtbl (Var: VarType) (Dom: Lattice.S) (VH: Hashtbl.S with type key = Var.t) = -struct - module Var = - struct - include Printable.Std - include Var - let name () = "var" - - let pretty = pretty_trace - include Printable.SimplePretty ( - struct - type nonrec t = t - let pretty = pretty - end - ) - end - - include PrecCompare.MakeHashtbl (Var) (Dom) (VH) -end - -module CompareEqSys (Sys: EqConstrSys) (VH: Hashtbl.S with type key = Sys.Var.t) = -struct - module Compare = CompareHashtbl (Sys.Var) (Sys.Dom) (VH) - - let compare (name1, name2) vh1 vh2 = - Logs.newline (); - Logs.info "Comparing EqConstrSys precision of %s (left) with %s (right):" name1 name2; - let verbose = get_bool "dbg.compare_runs.diff" in - let (_, msg) = Compare.compare ~verbose ~name1 vh1 ~name2 vh2 in - Logs.info "EqConstrSys comparison summary: %t" (fun () -> msg); - Logs.newline (); -end - -module CompareGlobal (GVar: VarType) (G: Lattice.S) (GH: Hashtbl.S with type key = GVar.t) = -struct - module Compare = CompareHashtbl (GVar) (G) (GH) - - let compare (name1, name2) vh1 vh2 = - Logs.newline (); - Logs.info "Comparing globals precision of %s (left) with %s (right):" name1 name2; - let verbose = get_bool "dbg.compare_runs.diff" in - let (_, msg) = Compare.compare ~verbose ~name1 vh1 ~name2 vh2 in - Logs.info "Globals comparison summary: %t" (fun () -> msg); - Logs.newline (); -end - -module CompareNode (C: Printable.S) (D: Lattice.S) (LH: Hashtbl.S with type key = VarF (C).t) = -struct - module Node = - struct - include Node - let var_id _ = "nodes" - let node x = x - let is_write_only _ = false - end - module NH = Hashtbl.Make (Node) - - module Compare = CompareHashtbl (Node) (D) (NH) - - let join_contexts (lh: D.t LH.t): D.t NH.t = - let nh = NH.create 113 in - LH.iter (fun (n, _) d -> - let d' = try D.join (NH.find nh n) d with Not_found -> d in - NH.replace nh n d' - ) lh; - nh - - let compare (name1, name2) vh1 vh2 = - Logs.newline (); - Logs.info "Comparing nodes precision of %s (left) with %s (right):" name1 name2; - let vh1' = join_contexts vh1 in - let vh2' = join_contexts vh2 in - let verbose = get_bool "dbg.compare_runs.diff" in - let (_, msg) = Compare.compare ~verbose ~name1 vh1' ~name2 vh2' in - Logs.info "Nodes comparison summary: %t" (fun () -> msg); - Logs.newline (); -end diff --git a/src/framework/control.ml b/src/framework/control.ml index 05fad90caf..1d0ebb869b 100644 --- a/src/framework/control.ml +++ b/src/framework/control.ml @@ -9,8 +9,9 @@ open Analyses open ConstrSys open GobConfig open Constraints +open SpecLifters -module type S2S = functor (X : Spec) -> Spec +module type S2S = Spec2Spec (* spec is lazy, so HConsed table in Hashcons lifters is preserved between analyses in server mode *) let spec_module: (module Spec) Lazy.t = lazy ( @@ -23,7 +24,7 @@ let spec_module: (module Spec) Lazy.t = lazy ( let module S1 = (val (module MCP.MCP2 : Spec) - |> lift (get_int "ana.context.gas_value" >= 0) (module ContextGasLifter) + |> lift (get_int "ana.context.gas_value" >= 0) (ContextGasLifter.get_gas_lifter ()) |> lift true (module WidenContextLifterSide) (* option checked in functor *) (* hashcons before witness to reduce duplicates, because witness re-uses contexts in domain and requires tag for PathSensitive3 *) |> lift (get_bool "ana.opt.hashcons" || arg_enabled) (module HashconsContextLifter) @@ -39,8 +40,8 @@ let spec_module: (module Spec) Lazy.t = lazy ( (* Widening tokens must be outside of hashcons, because widening token domain ignores token sets for identity, so hashcons doesn't allow adding tokens. Also must be outside of deadcode, because deadcode splits (like mutex lock event) don't pass on tokens. *) |> lift (get_bool "ana.widen.tokens") (module WideningTokens.Lifter) - |> lift true (module LongjmpLifter) - |> lift termination_enabled (module RecursionTermLifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) + |> lift true (module LongjmpLifter.Lifter) + |> lift termination_enabled (module RecursionTermLifter.Lifter) (* Always activate the recursion termination analysis, when the loop termination analysis is activated*) ) in GobConfig.building_spec := false; @@ -90,7 +91,7 @@ struct end module Slvr = (GlobSolverFromEqSolver (Goblint_solver.Selector.Make (PostSolverArg))) (EQSys) (LHT) (GHT) (* The comparator *) - module CompareGlobSys = Constraints.CompareGlobSys (SpecSys) + module CompareGlobSys = CompareConstraints.CompareGlobSys (SpecSys) (* Triple of the function, context, and the local value. *) module RT = AnalysisResult.ResultType2 (Spec) @@ -357,6 +358,7 @@ struct (* real beginning of the [analyze] function *) if get_bool "ana.sv-comp.enabled" then Witness.init (module FileCfg); (* TODO: move this out of analyze_loop *) + YamlWitness.init (); AnalysisState.global_initialization := true; GobConfig.earlyglobs := get_bool "exp.earlyglobs"; @@ -520,15 +522,15 @@ struct if get_bool "dbg.compare_runs.globsys" then CompareGlobSys.compare (d1, d2) r1 r2; - let module CompareEqSys = Constraints.CompareEqSys (S2) (VH) in + let module CompareEqSys = CompareConstraints.CompareEqSys (S2) (VH) in if get_bool "dbg.compare_runs.eqsys" then CompareEqSys.compare (d1, d2) r1' r2'; - let module CompareGlobal = Constraints.CompareGlobal (EQSys.GVar) (EQSys.G) (GHT) in + let module CompareGlobal = CompareConstraints.CompareGlobal (EQSys.GVar) (EQSys.G) (GHT) in if get_bool "dbg.compare_runs.global" then CompareGlobal.compare (d1, d2) (snd r1) (snd r2); - let module CompareNode = Constraints.CompareNode (Spec.C) (EQSys.D) (LHT) in + let module CompareNode = CompareConstraints.CompareNode (Spec.C) (EQSys.D) (LHT) in if get_bool "dbg.compare_runs.node" then CompareNode.compare (d1, d2) (fst r1) (fst r2); @@ -789,15 +791,19 @@ struct ); (* Before SV-COMP, so result can depend on YAML witness validation. *) - if get_string "witness.yaml.validate" <> "" then ( - let module YWitness = YamlWitness.Validator (R) in - YWitness.validate () - ); + let yaml_validate_result = + if get_string "witness.yaml.validate" <> "" then ( + let module YWitness = YamlWitness.Validator (R) in + Some (YWitness.validate ()) + ) + else + None + in if get_bool "ana.sv-comp.enabled" then ( (* SV-COMP and witness generation *) let module WResult = Witness.Result (R) in - WResult.write entrystates + WResult.write yaml_validate_result entrystates ); if get_bool "witness.yaml.enabled" then ( diff --git a/src/goblint.ml b/src/goblint.ml index a687badb8e..6f8f8c20e5 100644 --- a/src/goblint.ml +++ b/src/goblint.ml @@ -5,8 +5,8 @@ open Maingoblint (** the main function *) let main () = try - Cilfacade.init (); Maingoblint.parse_arguments (); + Cilfacade.init (); (* Timing. *) Maingoblint.reset_stats (); @@ -65,6 +65,7 @@ let main () = do_gobview file; do_stats (); Goblint_timing.teardown_tef (); + (* TODO: generalize exit codes for AnalysisState.unsound_both_branches_dead? *) if !AnalysisState.verified = Some false then exit 3 (* verifier failed! *) ) with @@ -83,6 +84,11 @@ let main () = Logs.error "%s" (MessageUtil.colorize ~fd:Unix.stderr ("{RED}Analysis was aborted because it reached the set timeout of " ^ get_string "dbg.timeout" ^ " or was signalled SIGPROF!")); Goblint_timing.teardown_tef (); exit 124 + | Svcomp.Error msg -> + do_stats (); + Witness.print_svcomp_result ("ERROR (" ^ msg ^ ")"); + Goblint_timing.teardown_tef (); + exit 1 (* We do this since the evaluation order of top-level bindings is not defined, but we want `main` to run after all the other side-effects (e.g. registering analyses/solvers) have happened. *) let () = at_exit main diff --git a/src/goblint_lib.ml b/src/goblint_lib.ml index 8013d9b2fe..91f9837419 100644 --- a/src/goblint_lib.ml +++ b/src/goblint_lib.ml @@ -23,6 +23,7 @@ module CfgTools = CfgTools module Analyses = Analyses module ConstrSys = ConstrSys module Constraints = Constraints +module CompareConstraints = CompareConstraints module AnalysisState = AnalysisState module AnalysisStateUtil = AnalysisStateUtil module ControlSpecC = ControlSpecC @@ -171,6 +172,19 @@ module AbortUnless = AbortUnless module PtranalAnalysis = PtranalAnalysis +(** {1 Analysis lifters} + + Transformations of analyses into extended analyses. *) + +module SpecLifters = SpecLifters +module LongjmpLifter = LongjmpLifter +module RecursionTermLifter = RecursionTermLifter +module ContextGasLifter = ContextGasLifter +module WideningTokens = WideningTokens + +module WitnessConstraints = WitnessConstraints + + (** {1 Domains} Domains used by analysis specifications and constraint systems are {{!Lattice.S} lattices}. @@ -326,7 +340,6 @@ module WitnessUtil = WitnessUtil Automaton-based GraphML witnesses used in SV-COMP. *) module MyARG = MyARG -module WitnessConstraints = WitnessConstraints module ArgTools = ArgTools module Witness = Witness module Graphml = Graphml @@ -337,7 +350,6 @@ module Graphml = Graphml module YamlWitness = YamlWitness module YamlWitnessType = YamlWitnessType -module WideningTokens = WideningTokens (** {3 Violation} diff --git a/src/incremental/compareAST.ml b/src/incremental/compareAST.ml index f3de153658..59adfe00be 100644 --- a/src/incremental/compareAST.ml +++ b/src/incremental/compareAST.ml @@ -73,7 +73,7 @@ let forward_list_equal ?(propF = (&&>>)) f l1 l2 ~(rename_mapping: rename_mappin let compare_name (a: string) (b: string) = let anon_struct = "__anonstruct_" in let anon_union = "__anonunion_" in - if a = b then true else BatString.(starts_with a anon_struct && starts_with b anon_struct || starts_with a anon_union && starts_with b anon_union) + if a = b then true else String.(starts_with a ~prefix:anon_struct && starts_with b ~prefix:anon_struct || starts_with a ~prefix:anon_union && starts_with b ~prefix:anon_union) let rec eq_constant ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) (a: constant) (b: constant) : bool * rename_mapping = match a, b with @@ -91,7 +91,7 @@ and eq_exp (a: exp) (b: exp) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ | AlignOf typ1, AlignOf typ2 -> eq_typ_acc typ1 typ2 ~rename_mapping ~acc | AlignOfE exp1, AlignOfE exp2 -> eq_exp exp1 exp2 ~rename_mapping ~acc | UnOp (op1, exp1, typ1), UnOp (op2, exp2, typ2) -> - ((op1 == op2), rename_mapping) &&>> eq_exp exp1 exp2 ~acc &&>> eq_typ_acc typ1 typ2 ~acc + (CilType.Unop.equal op1 op2, rename_mapping) &&>> eq_exp exp1 exp2 ~acc &&>> eq_typ_acc typ1 typ2 ~acc | BinOp (op1, left1, right1, typ1), BinOp (op2, left2, right2, typ2) -> (op1 = op2, rename_mapping) &&>> eq_exp left1 left2 ~acc &&>> eq_exp right1 right2 ~acc &&>> eq_typ_acc typ1 typ2 ~acc | CastE (typ1, exp1), CastE (typ2, exp2) -> eq_typ_acc typ1 typ2 ~rename_mapping ~acc &&>> eq_exp exp1 exp2 ~acc | AddrOf lv1, AddrOf lv2 -> eq_lval lv1 lv2 ~rename_mapping ~acc @@ -178,8 +178,8 @@ and eq_typ_acc ?(fun_parameter_name_comparison_enabled: bool = true) (a: typ) (b if Messages.tracing then Messages.traceu "compareast" "eq_typ_acc %a vs %a" d_type a d_type b; (r, updated_rename_mapping) -and eq_eitems (a: string * exp * location) (b: string * exp * location) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) = match a, b with - (name1, exp1, _l1), (name2, exp2, _l2) -> (name1 = name2, rename_mapping) &&>> eq_exp exp1 exp2 ~acc +and eq_eitems (a: string * attributes * exp * location) (b: string * attributes * exp * location) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) = match a, b with + (name1, attr1, exp1, _l1), (name2, attr2, exp2, _l2) -> (name1 = name2, rename_mapping) &&>> forward_list_equal (eq_attribute ~acc) attr1 attr2 &&>> eq_exp exp1 exp2 ~acc (* Ignore location *) and eq_enuminfo (a: enuminfo) (b: enuminfo) ~(rename_mapping: rename_mapping) ~(acc: (typ * typ) list) = @@ -210,6 +210,7 @@ and eq_attrparam (a: attrparam) (b: attrparam) ~(rename_mapping: rename_mapping) | AAddrOf attrparam1, AAddrOf attrparam2 -> eq_attrparam attrparam1 attrparam2 ~rename_mapping ~acc | AIndex (left1, right1), AIndex (left2, right2) -> eq_attrparam left1 left2 ~rename_mapping ~acc &&>> eq_attrparam right1 right2 ~acc | AQuestion (left1, middle1, right1), AQuestion (left2, middle2, right2) -> eq_attrparam left1 left2 ~rename_mapping ~acc &&>> eq_attrparam middle1 middle2 ~acc &&>> eq_attrparam right1 right2 ~acc + | AAssign (left1, right1), AAssign (left2, right2) -> eq_attrparam left1 left2 ~rename_mapping ~acc &&>> eq_attrparam right1 right2 ~acc | a, b -> a = b, rename_mapping and eq_attribute (a: attribute) (b: attribute) ~(acc: (typ * typ) list) ~(rename_mapping: rename_mapping) = match a, b with diff --git a/src/incremental/compareCFG.ml b/src/incremental/compareCFG.ml index 84b120b8e3..6c314ef7c9 100644 --- a/src/incremental/compareCFG.ml +++ b/src/incremental/compareCFG.ml @@ -18,7 +18,7 @@ let (&&<>) (prev_result: bool * rename_mapping) f : bool * rename_mapping = let eq_node (x, fun1) (y, fun2) ~rename_mapping = let isPseudoReturn f sid = let pid = Cilfacade.get_pseudo_return_id f in - sid == pid in + sid = pid in match x,y with | Statement s1, Statement s2 -> let p1 = isPseudoReturn fun1 s1.sid in diff --git a/src/incremental/compareCIL.ml b/src/incremental/compareCIL.ml index ea22e02a56..837bd65589 100644 --- a/src/incremental/compareCIL.ml +++ b/src/incremental/compareCIL.ml @@ -19,7 +19,7 @@ let name_of_global_col gc = match gc.def with | None -> raise (Failure "empty global record") let compare_global_col gc1 gc2 = compare (name_of_global_col gc1) (name_of_global_col gc2) -let equal_name_global_col gc1 gc2 = compare_global_col gc1 gc2 == 0 +let equal_name_global_col gc1 gc2 = compare_global_col gc1 gc2 = 0 let get_varinfo gc = match gc.decls, gc.def with | _, Some (Var v) -> v diff --git a/src/incremental/makefileUtil.ml b/src/incremental/makefileUtil.ml index 9a17122f5e..33cd8f64c9 100644 --- a/src/incremental/makefileUtil.ml +++ b/src/incremental/makefileUtil.ml @@ -39,7 +39,7 @@ let find_file_by_suffix (dir: Fpath.t) (file_name_suffix: string) = | (h::t) -> let f = Fpath.to_string h in if Sys.file_exists f && Sys.is_directory f then (Queue.add h dirs; search dir t) - else if Batteries.String.ends_with (Fpath.filename h) file_name_suffix then h else search dir t + else if String.ends_with (Fpath.filename h) ~suffix:file_name_suffix then h else search dir t | [] -> if Queue.is_empty dirs then failwith ("find_file_by_suffix found no files with suffix "^file_name_suffix^" in "^ Fpath.to_string dir) else let d = Queue.take dirs in search d (list_files d) diff --git a/src/lifters/contextGasLifter.ml b/src/lifters/contextGasLifter.ml new file mode 100644 index 0000000000..75bd9f7641 --- /dev/null +++ b/src/lifters/contextGasLifter.ml @@ -0,0 +1,142 @@ +(** Lifts a [Spec] with the context gas variable. The gas variable limits the number of context-sensitively analyzed function calls in a call stack. + For every function call the gas is reduced. If the gas is zero, the remaining function calls are analyzed without context-information *) + +open Batteries +open GoblintCil +open Analyses +open GobConfig + +module M = Messages + +module NoContext = struct let name = "no context" end + +module type Gas = sig + module M:Lattice.S + val startgas: unit -> M.t + val is_exhausted: fundec -> M.t -> bool + val callee_gas: fundec -> M.t -> M.t + val thread_gas: varinfo -> M.t -> M.t +end + +module ContextGasLifter (Gas:Gas) (S:Spec) + : Spec with module D = Lattice.Prod (S.D) (Gas.M) + and module C = Printable.Option (S.C) (NoContext) + and module G = S.G += +struct + include S + + module Context_Gas_Prod (Base1: Lattice.S) (Base2: Lattice.S) = + struct + include Lattice.Prod (Base1) (Base2) + let printXml f (x,y) = + BatPrintf.fprintf f "\n%a\n%a\n" Base1.printXml x Base2.printXml y + end + module D = Context_Gas_Prod (S.D) (Gas.M) (* Product of S.D and a value from the gas module, tracking the context gas value *) + module C = Printable.Option (S.C) (NoContext) + module G = S.G + module V = S.V + module P = + struct + include S.P + let of_elt (x, _) = of_elt x + end + + (* returns context gas value of the given ctx *) + let cg_val ctx = snd ctx.local + + type marshal = S.marshal + let init = S.init + let finalize = S.finalize + + + let startcontext () = Some (S.startcontext ()) + let name () = S.name ()^" with context gas" + let startstate v = S.startstate v, Gas.startgas () + let exitstate v = S.exitstate v, Gas.startgas () + let morphstate v (d,i) = S.morphstate v d, i + + let conv (ctx:(D.t,G.t,C.t,V.t) ctx): (S.D.t,G.t,S.C.t,V.t)ctx = + {ctx with local = fst ctx.local + ; split = (fun d es -> ctx.split (d, cg_val ctx) es) + ; context = (fun () -> match ctx.context () with Some c -> c | None -> ctx_failwith "no context (contextGas = 0)")} + + let context ctx fd (d,i) = + (* only keep context if the context gas is greater zero *) + if Gas.is_exhausted fd i then + None + else + Some (S.context (conv ctx) fd d) + + let enter ctx r f args = + let liftmap_tup = List.map (fun (x,y) -> (x, cg_val ctx), (y, Gas.callee_gas f (cg_val ctx))) in + liftmap_tup (S.enter (conv ctx) r f args) + + let threadenter ctx ~multiple lval f args = + let liftmap d = List.map (fun (x) -> (x, Gas.thread_gas f (cg_val ctx))) d in + liftmap (S.threadenter (conv ctx) ~multiple lval f args) + + let query ctx (type a) (q: a Queries.t):a Queries.result = + match q with + | Queries.GasExhausted f -> + let (d,i) = ctx.local in + Gas.is_exhausted f i + | _ -> S.query (conv ctx) q + + let sync ctx reason = S.sync (conv ctx) reason, cg_val ctx + let assign ctx lval expr = S.assign (conv ctx) lval expr, cg_val ctx + let vdecl ctx v = S.vdecl (conv ctx) v, cg_val ctx + let body ctx fundec = S.body (conv ctx) fundec, cg_val ctx + let branch ctx e tv = S.branch (conv ctx) e tv, cg_val ctx + let return ctx r f = S.return (conv ctx) r f, cg_val ctx + let asm ctx = S.asm (conv ctx), cg_val ctx + let skip ctx = S.skip (conv ctx), cg_val ctx + let special ctx r f args = S.special (conv ctx) r f args, cg_val ctx + let combine_env ctx r fe f args fc es f_ask = S.combine_env (conv ctx) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val ctx + let combine_assign ctx r fe f args fc es f_ask = S.combine_assign (conv ctx) r fe f args (Option.bind fc Fun.id) (fst es) f_ask, cg_val ctx + let paths_as_set ctx = List.map (fun (x) -> (x, cg_val ctx)) @@ S.paths_as_set (conv ctx) + let threadspawn ctx ~multiple lval f args fctx = S.threadspawn (conv ctx) ~multiple lval f args (conv fctx), cg_val ctx + let event ctx e octx = S.event (conv ctx) e (conv octx), cg_val ctx +end + +let get_gas_lifter () = + let module GasChain = Lattice.Chain (struct + (* Chain lattice has elements [0,n-1], but we want [0,gas_value] *) + let n () = get_int "ana.context.gas_value" + 1 + let names x = Format.asprintf "%d" x + end) + in + if get_string "ana.context.gas_scope" = "global" then + let module GlobalGas:Gas = struct + module M = GasChain + let startgas () = M.top () (* M.top () yields maximal gas value *) + + let is_exhausted _ v = v <= 0 + + (* callee gas = caller gas - 1 *) + let callee_gas f v = max 0 (v - 1) + let thread_gas f v = max 0 (v - 1) + end + in + (module ContextGasLifter(GlobalGas):Spec2Spec) + else + let module PerFunctionGas:Gas = struct + (* The order is reversed here to ensure that the minimum is used *) + (* 5 join 4 = 4 *) + module G = Lattice.Reverse(GasChain) + (* Missing bindings are bot, i.e., have maximal gas for this function *) + module M = MapDomain.MapBot_LiftTop(CilType.Fundec)(G) + let startgas () = M.empty () + let is_exhausted f v = GobOption.exists (fun g -> g <= 0) (M.find_opt f v) (* v <= 0 *) + let callee_gas f v = + let c = Option.default (G.bot ()) (M.find_opt f v) in + M.add f (max 0 c-1) v + let thread_gas f v = + match Cilfacade.find_varinfo_fundec f with + | fd -> + callee_gas fd v + | exception Not_found -> + callee_gas Cil.dummyFunDec v + end + in + (module ContextGasLifter(PerFunctionGas):Spec2Spec) diff --git a/src/lifters/contextGasLifter.mli b/src/lifters/contextGasLifter.mli new file mode 100644 index 0000000000..f5b07e2b35 --- /dev/null +++ b/src/lifters/contextGasLifter.mli @@ -0,0 +1,5 @@ +(** Lifts a [Spec] with the context gas variable. The gas variable limits the number of context-sensitively analyzed function calls in a call stack. + For every function call the gas is reduced. If the gas is zero, the remaining function calls are analyzed without context-information *) + +(** Gets the appropriate lifter (either local or per-function). Should only be called when context gas is active. *) +val get_gas_lifter : unit -> (module Analyses.Spec2Spec) diff --git a/src/lifters/longjmpLifter.ml b/src/lifters/longjmpLifter.ml new file mode 100644 index 0000000000..1a28085abe --- /dev/null +++ b/src/lifters/longjmpLifter.ml @@ -0,0 +1,247 @@ +open GoblintCil +open Analyses +open GobConfig + + +module Lifter (S: Spec): Spec = +struct + include S + + let name () = "Longjmp (" ^ S.name () ^ ")" + + module V = + struct + include Printable.Either3Conf (struct let expand1 = false let expand2 = true let expand3 = true end) (S.V) (Printable.Prod (Node) (C)) (Printable.Prod (CilType.Fundec) (C)) + let name () = "longjmp" + let s x = `Left x + let longjmpto x = `Middle x + let longjmpret x = `Right x + let is_write_only = function + | `Left x -> S.V.is_write_only x + | _ -> false + end + + module G = + struct + include Lattice.Lift2 (S.G) (S.D) + + let s = function + | `Bot -> S.G.bot () + | `Lifted1 x -> x + | _ -> failwith "LongjmpLifter.s" + let local = function + | `Bot -> S.D.bot () + | `Lifted2 x -> x + | _ -> failwith "LongjmpLifter.local" + let create_s s = `Lifted1 s + let create_local local = `Lifted2 local + + let printXml f = function + | `Lifted1 x -> S.G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" S.D.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x + end + + let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = + { ctx with + global = (fun v -> G.s (ctx.global (V.s v))); + sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); + } + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | WarnGlobal g -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (WarnGlobal (Obj.repr g)) + | _ -> + Queries.Result.top q + end + | InvariantGlobal g -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (InvariantGlobal (Obj.repr g)) + | _ -> + Queries.Result.top q + end + | IterSysVars (vq, vf) -> + (* vars for S *) + let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in + S.query (conv ctx) (IterSysVars (vq, vf')); + (* TODO: vars? *) + | _ -> + S.query (conv ctx) q + + + let branch ctx = S.branch (conv ctx) + let assign ctx = S.assign (conv ctx) + let vdecl ctx = S.vdecl (conv ctx) + let enter ctx = S.enter (conv ctx) + let paths_as_set ctx = S.paths_as_set (conv ctx) + let body ctx = S.body (conv ctx) + let return ctx = S.return (conv ctx) + let context ctx = S.context (conv ctx) + + let combine_env ctx lv e f args fc fd f_ask = + let conv_ctx = conv ctx in + let current_fundec = Node.find_fundec ctx.node in + let handle_longjmp (cd, fc, longfd) = + (* This is called per-path. *) + let rec cd_ctx = + { conv_ctx with + ask = (fun (type a) (q: a Queries.t) -> S.query cd_ctx q); + local = cd; + } + in + let longfd_ctx = + (* Inner scope to prevent unsynced longfd_ctx from being used. *) + (* Extra sync like with normal combine. *) + let rec sync_ctx = + { conv_ctx with + ask = (fun (type a) (q: a Queries.t) -> S.query sync_ctx q); + local = longfd; + prev_node = Function f; + } + in + let synced = S.sync sync_ctx `Join in + let rec longfd_ctx = + { sync_ctx with + ask = (fun (type a) (q: a Queries.t) -> S.query longfd_ctx q); + local = synced; + } + in + longfd_ctx + in + let combined = lazy ( (* does not depend on target, do at most once *) + (* Globals are non-problematic here, as they are always carried around without any issues! *) + (* A combine call is mostly needed to ensure locals have appropriate values. *) + (* Using f from called function on purpose here! Needed? *) + S.combine_env cd_ctx None e f args fc longfd_ctx.local (Analyses.ask_of_ctx longfd_ctx) (* no lval because longjmp return skips return value assignment *) + ) + in + let returned = lazy ( (* does not depend on target, do at most once *) + let rec combined_ctx = + { cd_ctx with + ask = (fun (type a) (q: a Queries.t) -> S.query combined_ctx q); + local = Lazy.force combined; + } + in + S.return combined_ctx None current_fundec + ) + in + let (active_targets, _) = longfd_ctx.ask ActiveJumpBuf in + let valid_targets = cd_ctx.ask ValidLongJmp in + let handle_target target = match target with + | JmpBufDomain.BufferEntryOrTop.AllTargets -> () (* The warning is already emitted at the point where the longjmp happens *) + | Target (target_node, target_context) -> + let target_fundec = Node.find_fundec target_node in + if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( + if M.tracing then Messages.tracel "longjmp" "Fun: Potentially from same context, side-effect to %a" Node.pretty target_node; + ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force combined)) + (* No need to propagate this outwards here, the set of valid longjumps is part of the context, we can never have the same context setting the longjmp multiple times *) + ) + (* Appropriate setjmp is not in current function & current context *) + else if JmpBufDomain.JmpBufSet.mem target valid_targets then + ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) + else + (* It actually is not handled here but was propagated here spuriously, we already warned at the location where this issue is caused *) + (* As the validlongjumps inside the callee is a a superset of the ones inside the caller *) + () + in + JmpBufDomain.JmpBufSet.iter handle_target active_targets + in + if M.tracing then M.tracel "longjmp" "longfd getg %a" CilType.Fundec.pretty f; + let longfd = G.local (ctx.global (V.longjmpret (f, Option.get fc))) in + if M.tracing then M.tracel "longjmp" "longfd %a" D.pretty longfd; + if not (D.is_bot longfd) then + handle_longjmp (ctx.local, fc, longfd); + S.combine_env (conv_ctx) lv e f args fc fd f_ask + + let combine_assign ctx lv e f args fc fd f_ask = + S.combine_assign (conv ctx) lv e f args fc fd f_ask + + let special ctx lv f args = + let conv_ctx = conv ctx in + match (LibraryFunctions.find f).special args with + | Setjmp {env} -> + (* Handling of returning for the first time *) + let normal_return = S.special conv_ctx lv f args in + let jmp_return = G.local (ctx.global (V.longjmpto (ctx.prev_node, ctx.context ()))) in + if S.D.is_bot jmp_return then + normal_return + else ( + let rec jmp_ctx = + { conv_ctx with + ask = (fun (type a) (q: a Queries.t) -> S.query jmp_ctx q); + local = jmp_return; + } + in + let longjmped = S.event jmp_ctx (Events.Longjmped {lval=lv}) jmp_ctx in + S.D.join normal_return longjmped + ) + | Longjmp {env; value} -> + let current_fundec = Node.find_fundec ctx.node in + let handle_path path = ( + let rec path_ctx = + { conv_ctx with + ask = (fun (type a) (q: a Queries.t) -> S.query path_ctx q); + local = path; + } + in + let specialed = lazy ( (* does not depend on target, do at most once *) + S.special path_ctx lv f args + ) + in + let returned = lazy ( (* does not depend on target, do at most once *) + let rec specialed_ctx = + { path_ctx with + ask = (fun (type a) (q: a Queries.t) -> S.query specialed_ctx q); + local = Lazy.force specialed; + } + in + S.return specialed_ctx None current_fundec + ) + in + (* Eval `env` again to avoid having to construct bespoke ctx to ask *) + let targets = path_ctx.ask (EvalJumpBuf env) in + let valid_targets = path_ctx.ask ValidLongJmp in + if M.tracing then Messages.tracel "longjmp" "Jumping to %a" JmpBufDomain.JmpBufSet.pretty targets; + let handle_target target = match target with + | JmpBufDomain.BufferEntryOrTop.AllTargets -> + M.warn ~category:Imprecise "Longjmp to potentially invalid target, as contents of buffer %a may be unknown! (imprecision due to heap?)" d_exp env; + M.msg_final Error ~category:Unsound ~tags:[Category Imprecise; Category Call] "Longjmp to unknown target ignored" + | Target (target_node, target_context) -> + let target_fundec = Node.find_fundec target_node in + if CilType.Fundec.equal target_fundec current_fundec && ControlSpecC.equal target_context (ctx.control_context ()) then ( + if M.tracing then Messages.tracel "longjmp" "Potentially from same context, side-effect to %a" Node.pretty target_node; + ctx.sideg (V.longjmpto (target_node, ctx.context ())) (G.create_local (Lazy.force specialed)) + ) + else if JmpBufDomain.JmpBufSet.mem target valid_targets then ( + if M.tracing then Messages.tracel "longjmp" "Longjmp to somewhere else, side-effect to %i" (S.C.hash (ctx.context ())); + ctx.sideg (V.longjmpret (current_fundec, ctx.context ())) (G.create_local (Lazy.force returned)) + ) + else + M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target! (Target %a in Function %a which may have already returned or is in a different thread)" Node.pretty target_node CilType.Fundec.pretty target_fundec + in + if JmpBufDomain.JmpBufSet.is_empty targets then + M.warn ~category:(Behavior (Undefined Other)) "Longjmp to potentially invalid target (%a is bot?!)" d_exp env + else + JmpBufDomain.JmpBufSet.iter handle_target targets + ) + in + List.iter handle_path (S.paths_as_set conv_ctx); + if !AnalysisState.should_warn && List.mem "termination" @@ get_string_list "ana.activated" then ( + AnalysisState.svcomp_may_not_terminate := true; + M.warn ~category:Termination "The program might not terminate! (Longjmp)" + ); + S.D.bot () + | _ -> S.special conv_ctx lv f args + let threadenter ctx = S.threadenter (conv ctx) + let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) + let sync ctx = S.sync (conv ctx) + let skip ctx = S.skip (conv ctx) + let asm ctx = S.asm (conv ctx) + let event ctx e octx = S.event (conv ctx) e (conv octx) +end diff --git a/src/lifters/longjmpLifter.mli b/src/lifters/longjmpLifter.mli new file mode 100644 index 0000000000..5fd74907bc --- /dev/null +++ b/src/lifters/longjmpLifter.mli @@ -0,0 +1,3 @@ +(** Analysis lifter for [longjmp] and [setjmp] support. *) + +module Lifter: Analyses.Spec2Spec diff --git a/src/lifters/recursionTermLifter.ml b/src/lifters/recursionTermLifter.ml new file mode 100644 index 0000000000..2d9f45de60 --- /dev/null +++ b/src/lifters/recursionTermLifter.ml @@ -0,0 +1,145 @@ +open GoblintCil +open Analyses + + +module Lifter (S: Spec) + : Spec with module D = S.D + and module C = S.C += +(* two global invariants: + - S.V -> S.G + Needed to store the previously built global invariants + - fundec * S.C -> (Set (fundec * S.C)) + The second global invariant maps from the callee fundec and context to a set of caller fundecs and contexts. + This structure therefore stores the context-sensitive call graph. + For example: + let the function f in context c call function g in context c'. + In the global invariant structure it would be stored like this: (g,c') -> {(f, c)} +*) + +struct + include S + + (* contains all the callee fundecs and contexts *) + module V = GVarFC(S.V)(S.C) + + (* Tuple containing the fundec and context of a caller *) + module Call = Printable.Prod (CilType.Fundec) (S.C) + + (* Set containing multiple caller tuples *) + module CallerSet = SetDomain.Make (Call) + + module G = + struct + include Lattice.Lift2 (G) (CallerSet) + + let spec = function + | `Bot -> G.bot () + | `Lifted1 x -> x + | _ -> failwith "RecursionTermLifter.spec" + + let callers = function + | `Bot -> CallerSet.bot () + | `Lifted2 x -> x + | _ -> failwith "RecursionTermLifter.callGraph" + + let create_spec spec = `Lifted1 spec + let create_singleton_caller caller = `Lifted2 (CallerSet.singleton caller) + + let printXml f = function + | `Lifted1 x -> G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" CallerSet.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x + + end + + let name () = "RecursionTermLifter (" ^ S.name () ^ ")" + + let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = + { ctx with + global = (fun v -> G.spec (ctx.global (V.spec v))); + sideg = (fun v g -> ctx.sideg (V.spec v) (G.create_spec g)); + } + + let cycleDetection ctx call = + let module LH = Hashtbl.Make (Printable.Prod (CilType.Fundec) (S.C)) in + let module LS = Set.Make (Printable.Prod (CilType.Fundec) (S.C)) in + (* find all cycles/SCCs *) + let global_visited_calls = LH.create 100 in + + (* DFS *) + let rec iter_call (path_visited_calls: LS.t) ((fundec, _) as call) = + if LS.mem call path_visited_calls then ( + AnalysisState.svcomp_may_not_terminate := true; (*set the indicator for a non-terminating program for the sv comp*) + (*Cycle found*) + let loc = M.Location.CilLocation fundec.svar.vdecl in + M.warn ~loc ~category:Termination "The program might not terminate! (Fundec %a is contained in a call graph cycle)" CilType.Fundec.pretty fundec) (* output a warning for non-termination*) + else if not (LH.mem global_visited_calls call) then begin + LH.replace global_visited_calls call (); + let new_path_visited_calls = LS.add call path_visited_calls in + let gvar = V.call call in + let callers = G.callers (ctx.global gvar) in + CallerSet.iter (fun to_call -> + iter_call new_path_visited_calls to_call + ) callers; + end + in + iter_call LS.empty call + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | WarnGlobal v -> + (* check result of loop analysis *) + if not (ctx.ask Queries.MustTermAllLoops) then + AnalysisState.svcomp_may_not_terminate := true; + let v: V.t = Obj.obj v in + begin match v with + | `Left v' -> + S.query (conv ctx) (WarnGlobal (Obj.repr v')) + | `Right call -> cycleDetection ctx call (* Note: to make it more efficient, one could only execute the cycle detection in case the loop analysis returns true, because otherwise the program will probably not terminate anyway*) + end + | InvariantGlobal v -> + let v: V.t = Obj.obj v in + begin match v with + | `Left v -> + S.query (conv ctx) (InvariantGlobal (Obj.repr v)) + | `Right v -> + Queries.Result.top q + end + | _ -> S.query (conv ctx) q + + let branch ctx = S.branch (conv ctx) + let assign ctx = S.assign (conv ctx) + let vdecl ctx = S.vdecl (conv ctx) + + + let record_call sideg callee caller = + sideg (V.call callee) (G.create_singleton_caller caller) + + let enter ctx = S.enter (conv ctx) + let context ctx = S.context (conv ctx) + let paths_as_set ctx = S.paths_as_set (conv ctx) + let body ctx = S.body (conv ctx) + let return ctx = S.return (conv ctx) + let combine_env ctx r fe f args fc es f_ask = + if !AnalysisState.postsolving then ( + let c_r: S.C.t = ctx.context () in (* Caller context *) + let nodeF = ctx.node in + let fd_r : fundec = Node.find_fundec nodeF in (* Caller fundec *) + let caller: (fundec * S.C.t) = (fd_r, c_r) in + let c_e: S.C.t = Option.get fc in (* Callee context *) + let fd_e : fundec = f in (* Callee fundec *) + let callee = (fd_e, c_e) in + record_call ctx.sideg callee caller + ); + S.combine_env (conv ctx) r fe f args fc es f_ask + + let combine_assign ctx = S.combine_assign (conv ctx) + let special ctx = S.special (conv ctx) + let threadenter ctx = S.threadenter (conv ctx) + let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) + let sync ctx = S.sync (conv ctx) + let skip ctx = S.skip (conv ctx) + let asm ctx = S.asm (conv ctx) + let event ctx e octx = S.event (conv ctx) e (conv octx) +end diff --git a/src/lifters/recursionTermLifter.mli b/src/lifters/recursionTermLifter.mli new file mode 100644 index 0000000000..059cea658f --- /dev/null +++ b/src/lifters/recursionTermLifter.mli @@ -0,0 +1,3 @@ +(** Cycle detection in the context-sensitive dynamic function call graph of an analysis. *) + +module Lifter: Analyses.Spec2Spec diff --git a/src/lifters/specLifters.ml b/src/lifters/specLifters.ml new file mode 100644 index 0000000000..e8292bedc0 --- /dev/null +++ b/src/lifters/specLifters.ml @@ -0,0 +1,791 @@ +(** Various simple and old analysis lifters. *) + +open Batteries +open GoblintCil +open Analyses +open GobConfig + + +(** Lifts a [Spec] so that the domain is [Hashcons]d *) +module HashconsLifter (S:Spec) + : Spec with module G = S.G + and module C = S.C += +struct + module HConsedArg = + struct + (* We do refine int values on narrow and meet {!IntDomain.IntDomTupleImpl}, which can lead to fixpoint issues if we assume x op x = x *) + (* see https://github.com/goblint/analyzer/issues/1005 *) + let assume_idempotent = GobConfig.get_string "ana.int.refinement" = "never" + end + module D = Lattice.HConsed (S.D) (HConsedArg) + module G = S.G + module C = S.C + module V = S.V + module P = + struct + include S.P + let of_elt x = of_elt (D.unlift x) + end + + let name () = S.name () ^" hashconsed" + + type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) + let init = S.init + let finalize = S.finalize + + let startstate v = D.lift (S.startstate v) + let exitstate v = D.lift (S.exitstate v) + let morphstate v d = D.lift (S.morphstate v (D.unlift d)) + + let conv ctx = + { ctx with local = D.unlift ctx.local + ; split = (fun d es -> ctx.split (D.lift d) es ) + } + + let context ctx fd = S.context (conv ctx) fd % D.unlift + let startcontext () = S.startcontext () + + let sync ctx reason = + D.lift @@ S.sync (conv ctx) reason + + let query ctx = + S.query (conv ctx) + + let assign ctx lv e = + D.lift @@ S.assign (conv ctx) lv e + + let vdecl ctx v = + D.lift @@ S.vdecl (conv ctx) v + + let branch ctx e tv = + D.lift @@ S.branch (conv ctx) e tv + + let body ctx f = + D.lift @@ S.body (conv ctx) f + + let return ctx r f = + D.lift @@ S.return (conv ctx) r f + + let asm ctx = + D.lift @@ S.asm (conv ctx) + + let skip ctx = + D.lift @@ S.skip (conv ctx) + + let enter ctx r f args = + List.map (fun (x,y) -> D.lift x, D.lift y) @@ S.enter (conv ctx) r f args + + let special ctx r f args = + D.lift @@ S.special (conv ctx) r f args + + let combine_env ctx r fe f args fc es f_ask = + D.lift @@ S.combine_env (conv ctx) r fe f args fc (D.unlift es) f_ask + + let combine_assign ctx r fe f args fc es f_ask = + D.lift @@ S.combine_assign (conv ctx) r fe f args fc (D.unlift es) f_ask + + let threadenter ctx ~multiple lval f args = + List.map D.lift @@ S.threadenter (conv ctx) ~multiple lval f args + + let threadspawn ctx ~multiple lval f args fctx = + D.lift @@ S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) + + let paths_as_set ctx = + List.map (fun x -> D.lift x) @@ S.paths_as_set (conv ctx) + + let event ctx e octx = + D.lift @@ S.event (conv ctx) e (conv octx) +end + +(** Lifts a [Spec] so that the context is [Hashcons]d. *) +module HashconsContextLifter (S:Spec) + : Spec with module D = S.D + and module G = S.G + and module C = Printable.HConsed (S.C) += +struct + module D = S.D + module G = S.G + module C = Printable.HConsed (S.C) + module V = S.V + module P = S.P + + let name () = S.name () ^" context hashconsed" + + type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) + let init = S.init + let finalize = S.finalize + + let startstate = S.startstate + let exitstate = S.exitstate + let morphstate = S.morphstate + + let conv ctx = + { ctx with context = (fun () -> C.unlift (ctx.context ())) } + + let context ctx fd = C.lift % S.context (conv ctx) fd + let startcontext () = C.lift @@ S.startcontext () + + let sync ctx reason = + S.sync (conv ctx) reason + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | Queries.IterPrevVars f -> + let g i (n, c, j) e = f i (n, Obj.repr (C.lift (Obj.obj c)), j) e in + S.query (conv ctx) (Queries.IterPrevVars g) + | _ -> S.query (conv ctx) q + + let assign ctx lv e = + S.assign (conv ctx) lv e + + let vdecl ctx v = + S.vdecl (conv ctx) v + + let branch ctx e tv = + S.branch (conv ctx) e tv + + let body ctx f = + S.body (conv ctx) f + + let return ctx r f = + S.return (conv ctx) r f + + let asm ctx = + S.asm (conv ctx) + + let skip ctx = + S.skip (conv ctx) + + let enter ctx r f args = + S.enter (conv ctx) r f args + + let special ctx r f args = + S.special (conv ctx) r f args + + let combine_env ctx r fe f args fc es f_ask = + S.combine_env (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask + + let combine_assign ctx r fe f args fc es f_ask = + S.combine_assign (conv ctx) r fe f args (Option.map C.unlift fc) es f_ask + + let threadenter ctx ~multiple lval f args = + S.threadenter (conv ctx) ~multiple lval f args + + let threadspawn ctx ~multiple lval f args fctx = + S.threadspawn (conv ctx) ~multiple lval f args (conv fctx) + + let paths_as_set ctx = S.paths_as_set (conv ctx) + let event ctx e octx = S.event (conv ctx) e (conv octx) +end + +(* see option ana.opt.equal *) +module OptEqual (S: Spec) = struct + module D = struct include S.D let equal x y = x == y || equal x y end + module G = struct include S.G let equal x y = x == y || equal x y end + module C = struct include S.C let equal x y = x == y || equal x y end + include (S : Spec with module D := D and module G := G and module C := C) +end + +(** If dbg.slice.on, stops entering functions after dbg.slice.n levels. *) +module LevelSliceLifter (S:Spec) + : Spec with module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) + and module G = S.G + and module C = S.C += +struct + module D = Lattice.Prod (S.D) (Lattice.Reverse (IntDomain.Lifted)) + module G = S.G + module C = S.C + module V = S.V + module P = + struct + include S.P + let of_elt (x, _) = of_elt x + end + + let name () = S.name ()^" level sliced" + + let start_level = ref (`Top) + + type marshal = S.marshal (* TODO: should hashcons table be in here to avoid relift altogether? *) + let init marshal = + if get_bool "dbg.slice.on" then + start_level := `Lifted (Int64.of_int (get_int "dbg.slice.n")); + S.init marshal + + let finalize = S.finalize + + let startstate v = (S.startstate v, !start_level) + let exitstate v = (S.exitstate v, !start_level) + let morphstate v (d,l) = (S.morphstate v d, l) + + let conv ctx = + { ctx with local = fst ctx.local + ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) + } + + let context ctx fd (d,_) = S.context (conv ctx) fd d + let startcontext () = S.startcontext () + + let lift_fun ctx f g h = + f @@ h (g (conv ctx)) + + let enter' ctx r f args = + let liftmap = List.map (fun (x,y) -> (x, snd ctx.local), (y, snd ctx.local)) in + lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) + + let lift ctx d = (d, snd ctx.local) + let lift_start_level d = (d, !start_level) + + let sync ctx reason = lift_fun ctx (lift ctx) S.sync ((|>) reason) + let query' ctx (type a) (q: a Queries.t): a Queries.result = + lift_fun ctx identity S.query (fun x -> x q) + let assign ctx lv e = lift_fun ctx (lift ctx) S.assign ((|>) e % (|>) lv) + let vdecl ctx v = lift_fun ctx (lift ctx) S.vdecl ((|>) v) + let branch ctx e tv = lift_fun ctx (lift ctx) S.branch ((|>) tv % (|>) e) + let body ctx f = lift_fun ctx (lift ctx) S.body ((|>) f) + let return ctx r f = lift_fun ctx (lift ctx) S.return ((|>) f % (|>) r) + let asm ctx = lift_fun ctx (lift ctx) S.asm identity + let skip ctx = lift_fun ctx (lift ctx) S.skip identity + let special ctx r f args = lift_fun ctx (lift ctx) S.special ((|>) args % (|>) f % (|>) r) + let combine_env' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) + let combine_assign' ctx r fe f args fc es f_ask = lift_fun ctx (lift ctx) S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) + + let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map lift_start_level) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) + let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (lift ctx) (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) + + let leq0 = function + | `Top -> false + | `Lifted x -> x <= 0L + | `Bot -> true + + let sub1 = function + | `Lifted x -> `Lifted (Int64.sub x 1L) + | x -> x + + let add1 = function + | `Lifted x -> `Lifted (Int64.add x 1L) + | x -> x + + let paths_as_set ctx = + let liftmap = List.map (fun x -> (x, snd ctx.local)) in + lift_fun ctx liftmap S.paths_as_set (Fun.id) + + let event ctx e octx = + lift_fun ctx (lift ctx) S.event ((|>) (conv octx) % (|>) e) + + let enter ctx r f args = + let (d,l) = ctx.local in + if leq0 l then + [ctx.local, D.bot ()] + else + enter' {ctx with local=(d, sub1 l)} r f args + + let combine_env ctx r fe f args fc es f_ask = + let (d,l) = ctx.local in + let l = add1 l in + if leq0 l then + (d, l) + else + let d',_ = combine_env' ctx r fe f args fc es f_ask in + (d', l) + + let combine_assign ctx r fe f args fc es f_ask = + let (d,l) = ctx.local in + (* No need to add1 here, already done in combine_env. *) + if leq0 l then + (d, l) + else + let d',_ = combine_assign' ctx r fe f args fc es f_ask in + (d', l) + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | Queries.EvalFunvar e -> + let (d,l) = ctx.local in + if leq0 l then + Queries.AD.empty () + else + query' ctx (Queries.EvalFunvar e) + | q -> query' ctx q +end + + +(** Limits the number of widenings per node. *) +module LimitLifter (S:Spec) = +struct + include (S : module type of S with module D := S.D and type marshal = S.marshal) + + let name () = S.name ()^" limited" + + let limit = ref 0 + + let init marshal = + limit := get_int "dbg.limit.widen"; + S.init marshal + + module H = MyCFG.NodeH + let h = H.create 13 + let incr k = + H.modify_def 1 k (fun v -> + if v >= !limit then failwith (GobPretty.sprintf "LimitLifter: Reached limit (%d) for node %a" !limit Node.pretty_plain_short (Option.get !MyCFG.current_node)); + v+1 + ) h; + module D = struct + include S.D + let widen x y = Option.may incr !MyCFG.current_node; widen x y (* when is this None? *) + end +end + + +(* widening on contexts, keeps contexts for calls only in D *) +module WidenContextLifterSide (S:Spec) += +struct + module DD = + struct + include S.D + let printXml f d = BatPrintf.fprintf f "%a" printXml d + end + module M = MapDomain.MapBot (Basetype.Variables) (DD) (* should be CilFun -> S.C, but CilFun is not Groupable, and S.C is no Lattice *) + + module D = struct + include Lattice.Prod (S.D) (M) + let printXml f (d,m) = BatPrintf.fprintf f "\n%a\n%a\n" S.D.printXml d M.printXml m + end + module G = S.G + module C = S.C + module V = S.V + module P = + struct + include S.P + let of_elt (x, _) = of_elt x + end + + + let name () = S.name ()^" with widened contexts" + + type marshal = S.marshal + let init = S.init + let finalize = S.finalize + + let inj f x = f x, M.bot () + + let startcontext () = S.startcontext () + let startstate = inj S.startstate + let exitstate = inj S.exitstate + let morphstate v (d,m) = S.morphstate v d, m + + + let conv ctx = + { ctx with local = fst ctx.local + ; split = (fun d es -> ctx.split (d, snd ctx.local) es ) + } + + let context ctx fd (d,m) = S.context (conv ctx) fd d (* just the child analysis' context *) + + let lift_fun ctx f g = g (f (conv ctx)), snd ctx.local + + let sync ctx reason = lift_fun ctx S.sync ((|>) reason) + let query ctx = S.query (conv ctx) + let assign ctx lv e = lift_fun ctx S.assign ((|>) e % (|>) lv) + let vdecl ctx v = lift_fun ctx S.vdecl ((|>) v) + let branch ctx e tv = lift_fun ctx S.branch ((|>) tv % (|>) e) + let body ctx f = lift_fun ctx S.body ((|>) f) + let return ctx r f = lift_fun ctx S.return ((|>) f % (|>) r) + let asm ctx = lift_fun ctx S.asm identity + let skip ctx = lift_fun ctx S.skip identity + let special ctx r f args = lift_fun ctx S.special ((|>) args % (|>) f % (|>) r) + + let event ctx e octx = lift_fun ctx S.event ((|>) (conv octx) % (|>) e) + + let threadenter ctx ~multiple lval f args = S.threadenter (conv ctx) ~multiple lval f args |> List.map (fun d -> (d, snd ctx.local)) + let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) + + let enter ctx r f args = + let m = snd ctx.local in + let d' v_cur = + if ContextUtil.should_keep ~isAttr:GobContext ~keepOption:"ana.context.widen" ~keepAttr:"widen" ~removeAttr:"no-widen" f then ( + let v_old = M.find f.svar m in (* S.D.bot () if not found *) + let v_new = S.D.widen v_old (S.D.join v_old v_cur) in + Messages.(if tracing && not (S.D.equal v_old v_new) then tracel "widen-context" "enter results in new context for function %s" f.svar.vname); + v_new, M.add f.svar v_new m + ) + else + v_cur, m + in + S.enter (conv ctx) r f args + |> List.map (fun (c,v) -> (c,m), d' v) (* c: caller, v: callee *) + + let paths_as_set ctx = + let m = snd ctx.local in + S.paths_as_set (conv ctx) |> List.map (fun v -> (v,m)) + + let combine_env ctx r fe f args fc es f_ask = lift_fun ctx S.combine_env (fun p -> p r fe f args fc (fst es) f_ask) + let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx S.combine_assign (fun p -> p r fe f args fc (fst es) f_ask) +end + + +(** Lifts a [Spec] with a special bottom element that represent unreachable code. *) +module DeadCodeLifter (S:Spec) + : Spec with module D = Dom (S.D) + and module G = S.G + and module C = S.C += +struct + module D = Dom (S.D) + module G = S.G + module C = S.C + module V = S.V + module P = + struct + include Printable.Option (S.P) (struct let name = "None" end) + + let of_elt = function + | `Lifted x -> Some (S.P.of_elt x) + | _ -> None + end + + let name () = S.name ()^" lifted" + + type marshal = S.marshal + let init = S.init + let finalize = S.finalize + + + let startcontext () = S.startcontext () + let startstate v = `Lifted (S.startstate v) + let exitstate v = `Lifted (S.exitstate v) + let morphstate v d = try `Lifted (S.morphstate v (D.unlift d)) with Deadcode -> d + + + let conv ctx = + { ctx with local = D.unlift ctx.local + ; split = (fun d es -> ctx.split (D.lift d) es ) + } + + let context ctx fd = S.context (conv ctx) fd % D.unlift + + let lift_fun ctx f g h b = + try f @@ h (g (conv ctx)) + with Deadcode -> b + + let sync ctx reason = lift_fun ctx D.lift S.sync ((|>) reason) `Bot + + let enter ctx r f args = + let liftmap = List.map (fun (x,y) -> D.lift x, D.lift y) in + lift_fun ctx liftmap S.enter ((|>) args % (|>) f % (|>) r) [] + + let paths_as_set ctx = + let liftmap = List.map (fun x -> D.lift x) in + lift_fun ctx liftmap S.paths_as_set (Fun.id) [D.bot ()] (* One dead path instead of none, such that combine_env gets called for functions with dead normal return (and thus longjmpy returns can be correctly handled by lifter). *) + + let query ctx (type a) (q: a Queries.t): a Queries.result = + lift_fun ctx identity S.query (fun (x) -> x q) (Queries.Result.bot q) + let assign ctx lv e = lift_fun ctx D.lift S.assign ((|>) e % (|>) lv) `Bot + let vdecl ctx v = lift_fun ctx D.lift S.vdecl ((|>) v) `Bot + let branch ctx e tv = lift_fun ctx D.lift S.branch ((|>) tv % (|>) e) `Bot + let body ctx f = lift_fun ctx D.lift S.body ((|>) f) `Bot + let return ctx r f = lift_fun ctx D.lift S.return ((|>) f % (|>) r) `Bot + let asm ctx = lift_fun ctx D.lift S.asm identity `Bot + let skip ctx = lift_fun ctx D.lift S.skip identity `Bot + let special ctx r f args = lift_fun ctx D.lift S.special ((|>) args % (|>) f % (|>) r) `Bot + let combine_env ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_env (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot + let combine_assign ctx r fe f args fc es f_ask = lift_fun ctx D.lift S.combine_assign (fun p -> p r fe f args fc (D.unlift es) f_ask) `Bot + + let threadenter ctx ~multiple lval f args = lift_fun ctx (List.map D.lift) (S.threadenter ~multiple) ((|>) args % (|>) f % (|>) lval) [] + let threadspawn ctx ~multiple lval f args fctx = lift_fun ctx D.lift (S.threadspawn ~multiple) ((|>) (conv fctx) % (|>) args % (|>) f % (|>) lval) `Bot + + let event (ctx:(D.t,G.t,C.t,V.t) ctx) (e:Events.t) (octx:(D.t,G.t,C.t,V.t) ctx):D.t = lift_fun ctx D.lift S.event ((|>) (conv octx) % (|>) e) `Bot +end + + +(** Add path sensitivity to a analysis *) +module PathSensitive2 (Spec:Spec) + : Spec + with module G = Spec.G + and module C = Spec.C + and module V = Spec.V += +struct + module D = + struct + (* TODO is it really worth it to check every time instead of just using sets and joining later? *) + module R = + struct + include Spec.P + type elt = Spec.D.t + end + module J = SetDomain.Joined (Spec.D) + include DisjointDomain.ProjectiveSet (Spec.D) (J) (R) + let name () = "PathSensitive (" ^ name () ^ ")" + + let printXml f x = + let print_one x = + BatPrintf.fprintf f "\n%a" Spec.D.printXml x + in + iter print_one x + end + + module G = Spec.G + module C = Spec.C + module V = Spec.V + module P = UnitP + + let name () = "PathSensitive2("^Spec.name ()^")" + + type marshal = Spec.marshal + let init = Spec.init + let finalize = Spec.finalize + + let startcontext () = Spec.startcontext () + let exitstate v = D.singleton (Spec.exitstate v) + let startstate v = D.singleton (Spec.startstate v) + let morphstate v d = D.map (Spec.morphstate v) d + + let conv ctx x = + let rec ctx' = { ctx with ask = (fun (type a) (q: a Queries.t) -> Spec.query ctx' q) + ; local = x + ; split = (ctx.split % D.singleton) } + in + ctx' + + let context ctx fd l = + if D.cardinal l <> 1 then + failwith "PathSensitive2.context must be called with a singleton set." + else + let x = D.choose l in + Spec.context (conv ctx x) fd x + + + let map ctx f g = + let h x xs = + try D.add (g (f (conv ctx x))) xs + with Deadcode -> xs + in + let d = D.fold h ctx.local (D.empty ()) in + if D.is_bot d then raise Deadcode else d + + let fold' ctx f g h a = + let k x a = + try h a @@ g @@ f @@ conv ctx x + with Deadcode -> a + in + D.fold k ctx.local a + + let assign ctx l e = map ctx Spec.assign (fun h -> h l e ) + let vdecl ctx v = map ctx Spec.vdecl (fun h -> h v) + let body ctx f = map ctx Spec.body (fun h -> h f ) + let return ctx e f = map ctx Spec.return (fun h -> h e f ) + let branch ctx e tv = map ctx Spec.branch (fun h -> h e tv) + let asm ctx = map ctx Spec.asm identity + let skip ctx = map ctx Spec.skip identity + let special ctx l f a = map ctx Spec.special (fun h -> h l f a) + + let event ctx e octx = + let fd1 = D.choose octx.local in + map ctx Spec.event (fun h -> h e (conv octx fd1)) + + let threadenter ctx ~multiple lval f args = + let g xs ys = (List.map (fun y -> D.singleton y) ys) @ xs in + fold' ctx (Spec.threadenter ~multiple) (fun h -> h lval f args) g [] + + let threadspawn ctx ~multiple lval f args fctx = + let fd1 = D.choose fctx.local in + map ctx (Spec.threadspawn ~multiple) (fun h -> h lval f args (conv fctx fd1)) + + let sync ctx reason = map ctx Spec.sync (fun h -> h reason) + + let query ctx (type a) (q: a Queries.t): a Queries.result = + (* TODO: handle Invariant path like PathSensitive3? *) + (* join results so that they are sound for all paths *) + let module Result = (val Queries.Result.lattice q) in + fold' ctx Spec.query identity (fun x f -> Result.join x (f q)) (Result.bot ()) + + let enter ctx l f a = + let g xs ys = (List.map (fun (x,y) -> D.singleton x, D.singleton y) ys) @ xs in + fold' ctx Spec.enter (fun h -> h l f a) g [] + + let paths_as_set ctx = + (* Path-sensitivity is only here, not below! *) + let elems = D.elements ctx.local in + List.map (D.singleton) elems + + let combine_env ctx l fe f a fc d f_ask = + assert (D.cardinal ctx.local = 1); + let cd = D.choose ctx.local in + let k x y = + if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; + try + let r = Spec.combine_env (conv ctx cd) l fe f a fc x f_ask in + if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; + D.add r y + with Deadcode -> + if M.tracing then M.traceu "combine" "combined function: dead"; + y + in + let d = D.fold k d (D.bot ()) in + if D.is_bot d then raise Deadcode else d + + let combine_assign ctx l fe f a fc d f_ask = + assert (D.cardinal ctx.local = 1); + let cd = D.choose ctx.local in + let k x y = + if M.tracing then M.traceli "combine" "function: %a" Spec.D.pretty x; + try + let r = Spec.combine_assign (conv ctx cd) l fe f a fc x f_ask in + if M.tracing then M.traceu "combine" "combined function: %a" Spec.D.pretty r; + D.add r y + with Deadcode -> + if M.tracing then M.traceu "combine" "combined function: dead"; + y + in + let d = D.fold k d (D.bot ()) in + if D.is_bot d then raise Deadcode else d +end + +module DeadBranchLifter (S: Spec): Spec = +struct + include S + + let name () = "DeadBranch (" ^ S.name () ^ ")" + + (* Two global invariants: + 1. S.V -> S.G -- used for S + 2. node -> (exp -> flat bool) -- used for warnings *) + + module V = + struct + include Printable.EitherConf (struct let expand1 = false let expand2 = true end) (S.V) (Node) + let name () = "DeadBranch" + let s x = `Left x + let node x = `Right x + let is_write_only = function + | `Left x -> S.V.is_write_only x + | `Right _ -> true + end + + module EM = + struct + include MapDomain.MapBot (Basetype.CilExp) (BoolDomain.FlatBool) + let name () = "branches" + end + + module G = + struct + include Lattice.Lift2 (S.G) (EM) + let name () = "deadbranch" + + let s = function + | `Bot -> S.G.bot () + | `Lifted1 x -> x + | _ -> failwith "DeadBranchLifter.s" + let node = function + | `Bot -> EM.bot () + | `Lifted2 x -> x + | _ -> failwith "DeadBranchLifter.node" + let create_s s = `Lifted1 s + let create_node node = `Lifted2 node + + let printXml f = function + | `Lifted1 x -> S.G.printXml f x + | `Lifted2 x -> BatPrintf.fprintf f "%a" EM.printXml x + | x -> BatPrintf.fprintf f "%a" printXml x + end + + let init marshal = + init marshal; + AnalysisState.unsound_both_branches_dead := Some false + + let conv (ctx: (_, G.t, _, V.t) ctx): (_, S.G.t, _, S.V.t) ctx = + { ctx with + global = (fun v -> G.s (ctx.global (V.s v))); + sideg = (fun v g -> ctx.sideg (V.s v) (G.create_s g)); + } + + let query ctx (type a) (q: a Queries.t): a Queries.result = + match q with + | WarnGlobal g -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (WarnGlobal (Obj.repr g)) + | `Right g -> + let em = G.node (ctx.global (V.node g)) in + EM.iter (fun exp tv -> + match tv with + | `Lifted tv -> + let loc = Node.location g in (* TODO: looking up location now doesn't work nicely with incremental *) + let cilinserted = if loc.synthetic then "(possibly inserted by CIL) " else "" in + M.warn ~loc:(Node g) ~tags:[CWE (if tv then 571 else 570)] ~category:Deadcode "condition '%a' %sis always %B" d_exp exp cilinserted tv + | `Bot when not (CilType.Exp.equal exp one) -> (* all branches dead *) + AnalysisState.unsound_both_branches_dead := Some true; + M.msg_final Error ~category:Analyzer ~tags:[Category Unsound] "Both branches dead"; + M.error ~loc:(Node g) ~category:Analyzer ~tags:[Category Unsound] "both branches over condition '%a' are dead" d_exp exp + | `Bot (* all branches dead, fine at our inserted Neg(1)-s because no Pos(1) *) + | `Top -> (* may be both true and false *) + () + ) em; + end + | InvariantGlobal g -> + let g: V.t = Obj.obj g in + begin match g with + | `Left g -> + S.query (conv ctx) (InvariantGlobal (Obj.repr g)) + | `Right g -> + Queries.Result.top q + end + | IterSysVars (vq, vf) -> + (* vars for S *) + let vf' x = vf (Obj.repr (V.s (Obj.obj x))) in + S.query (conv ctx) (IterSysVars (vq, vf')); + + (* node vars for dead branches *) + begin match vq with + | Node {node; _} -> + vf (Obj.repr (V.node node)) + | _ -> + () + end + | _ -> + S.query (conv ctx) q + + + let branch ctx = S.branch (conv ctx) + let context ctx = S.context (conv ctx) + + let branch ctx exp tv = + if !AnalysisState.postsolving then ( + try + let r = branch ctx exp tv in + (* branch is live *) + ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp (`Lifted tv))); (* record expression with reached tv *) + r + with Deadcode -> + (* branch is dead *) + ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.singleton exp `Bot)); (* record expression without reached tv *) + raise Deadcode + ) + else ( + ctx.sideg (V.node ctx.prev_node) (G.create_node (EM.bot ())); (* create global variable during solving, to allow postsolving leq hack to pass verify *) + branch ctx exp tv + ) + + let assign ctx = S.assign (conv ctx) + let vdecl ctx = S.vdecl (conv ctx) + let enter ctx = S.enter (conv ctx) + let paths_as_set ctx = S.paths_as_set (conv ctx) + let body ctx = S.body (conv ctx) + let return ctx = S.return (conv ctx) + let combine_env ctx = S.combine_env (conv ctx) + let combine_assign ctx = S.combine_assign (conv ctx) + let special ctx = S.special (conv ctx) + let threadenter ctx = S.threadenter (conv ctx) + let threadspawn ctx ~multiple lv f args fctx = S.threadspawn (conv ctx) ~multiple lv f args (conv fctx) + let sync ctx = S.sync (conv ctx) + let skip ctx = S.skip (conv ctx) + let asm ctx = S.asm (conv ctx) + let event ctx e octx = S.event (conv ctx) e (conv octx) +end diff --git a/src/util/wideningTokens.ml b/src/lifters/wideningTokens.ml similarity index 100% rename from src/util/wideningTokens.ml rename to src/lifters/wideningTokens.ml diff --git a/src/ppx/dune b/src/ppx/dune new file mode 100644 index 0000000000..ff757cb8ca --- /dev/null +++ b/src/ppx/dune @@ -0,0 +1 @@ +(include_subdirs no) diff --git a/src/ppx/lattice/dune b/src/ppx/lattice/dune new file mode 100644 index 0000000000..862298be13 --- /dev/null +++ b/src/ppx/lattice/dune @@ -0,0 +1,5 @@ +(library + (name ppx_deriving_lattice) + (kind ppx_deriver) + (libraries ppxlib ppx_easy_deriving) + (preprocess (pps ppxlib.metaquot))) diff --git a/src/ppx/lattice/ppx_deriving_lattice.ml b/src/ppx/lattice/ppx_deriving_lattice.ml new file mode 100644 index 0000000000..e78e88644a --- /dev/null +++ b/src/ppx/lattice/ppx_deriving_lattice.ml @@ -0,0 +1,97 @@ +open Ppxlib +open Ppx_easy_deriving + +module LeqArg: Product.Reduce.Conjunctive.S = +struct + let name = "leq" +end + +module LeqDeriver = Deriver.Make (Product.Reduce2.Make (Product.Reduce.Conjunctive.Make (LeqArg))) +let leq_deriving = LeqDeriver.register () + + +module JoinArg: Product.Map2.S = +struct + let name = "join" +end + +module JoinDeriver = Deriver.Make (Product.Map2.Make (JoinArg)) +let join_deriving = JoinDeriver.register () + + +module MeetArg: Product.Map2.S = +struct + let name = "meet" +end + +module MeetDeriver = Deriver.Make (Product.Map2.Make (MeetArg)) +let meet_deriving = MeetDeriver.register () + + +module WidenArg: Product.Map2.S = +struct + let name = "widen" +end + +module WidenDeriver = Deriver.Make (Product.Map2.Make (WidenArg)) +let widen_deriving = WidenDeriver.register () + + +module NarrowArg: Product.Map2.S = +struct + let name = "narrow" +end + +module NarrowDeriver = Deriver.Make (Product.Map2.Make (NarrowArg)) +let narrow_deriving = NarrowDeriver.register () + + +module BotArg: Product.Create.S = +struct + let name = "bot" + let typ ~loc _ = [%type: unit] +end + +module BotDeriver = Deriver.Make (Product.Create.Make (BotArg)) +let bot_deriving = BotDeriver.register () + + +module IsBotArg: Product.Reduce.Conjunctive.S = +struct + let name = "is_bot" +end + +module IsBotDeriver = Deriver.Make (Product.Reduce1.Make (Product.Reduce.Conjunctive.Make (IsBotArg))) +let is_bot_deriving = IsBotDeriver.register () + + +module TopArg: Product.Create.S = +struct + let name = "top" + let typ ~loc _ = [%type: unit] +end + +module TopDeriver = Deriver.Make (Product.Create.Make (TopArg)) +let top_deriving = TopDeriver.register () + + +module IsTopArg: Product.Reduce.Conjunctive.S = +struct + let name = "is_top" +end + +module IsTopDeriver = Deriver.Make (Product.Reduce1.Make (Product.Reduce.Conjunctive.Make (IsTopArg))) +let is_top_deriving = IsTopDeriver.register () + + +let _ = Ppxlib.Deriving.add_alias "lattice" [ + leq_deriving; + join_deriving; + meet_deriving; + widen_deriving; + narrow_deriving; + bot_deriving; + is_bot_deriving; + top_deriving; + is_top_deriving; + ] diff --git a/src/ppx/printable/dune b/src/ppx/printable/dune new file mode 100644 index 0000000000..8e08232de6 --- /dev/null +++ b/src/ppx/printable/dune @@ -0,0 +1,5 @@ +(library + (name ppx_deriving_printable) + (kind ppx_deriver) + (libraries ppxlib ppx_easy_deriving) + (preprocess (pps ppxlib.metaquot))) diff --git a/src/ppx/printable/ppx_deriving_printable.ml b/src/ppx/printable/ppx_deriving_printable.ml new file mode 100644 index 0000000000..75fd5044fe --- /dev/null +++ b/src/ppx/printable/ppx_deriving_printable.ml @@ -0,0 +1,16 @@ +open Ppx_easy_deriving + +module ReliftArg: Product.Map1.S = +struct + let name = "relift" +end + +(* TODO: Map1 should also do variants *) +module ReliftDeriver = Deriver.Make (Product.Map1.Make (ReliftArg)) +let relift_deriving = ReliftDeriver.register () + + +(* TODO: needs https://github.com/ocaml-ppx/ppxlib/pull/124 to include eq, ord, hash *) +(* let _ = Ppxlib.Deriving.add_alias "printable" [ + relift_deriving; + ] *) diff --git a/src/solver/topDown_deprecated.ml b/src/solver/topDown_deprecated.ml index 16c45fcd16..a46da9e441 100644 --- a/src/solver/topDown_deprecated.ml +++ b/src/solver/topDown_deprecated.ml @@ -4,8 +4,6 @@ open Batteries open ConstrSys open Messages -exception SolverCannotDoGlobals - (** modified SLR3 as top down solver *) module TD3 = diff --git a/src/transform/evalAssert.ml b/src/transform/evalAssert.ml index eab06222ef..8f858d09df 100644 --- a/src/transform/evalAssert.ml +++ b/src/transform/evalAssert.ml @@ -22,12 +22,8 @@ open Formatcil will be removed and they will fail on the next iteration *) -module EvalAssert = struct - (* should asserts be surrounded by __VERIFIER_atomic_{begin,end}? *) - let surroundByAtomic = true - - (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) - let ass = makeVarinfo true "__VERIFIER_assert" (TVoid []) +module EvalAssert = +struct let atomicBegin = makeVarinfo true "__VERIFIER_atomic_begin" (TVoid []) let atomicEnd = makeVarinfo true "__VERIFIER_atomic_end" (TVoid []) @@ -39,10 +35,16 @@ module EvalAssert = struct val emit_after_lock = GobConfig.get_bool "witness.invariant.after-lock" val emit_other = GobConfig.get_bool "witness.invariant.other" + (* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *) + val assert_function = makeVarinfo true (GobConfig.get_string "trans.assert.function") (TVoid []) + (* should asserts be surrounded by __VERIFIER_atomic_{begin,end}? *) + val surroundByAtomic = GobConfig.get_bool "trans.assert.wrap-atomic" + method! vstmt s = let is_lock exp args = match exp with | Lval(Var v,_) when LibraryFunctions.is_special v -> + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo v) @@ fun () -> let desc = LibraryFunctions.find v in (match desc.special args with | Lock _ -> true @@ -59,7 +61,7 @@ module EvalAssert = struct match (ask ~node loc).f (Queries.Invariant context) with | `Lifted e -> let es = WitnessUtil.InvariantExp.process_exp e in - let asserts = List.map (fun e -> cInstr ("%v:assert (%e:exp);") loc [("assert", Fv ass); ("exp", Fe e)]) es in + let asserts = List.map (fun e -> cInstr ("%v:assert (%e:exp);") loc [("assert", Fv assert_function); ("exp", Fe e)]) es in if surroundByAtomic then let abegin = (cInstr ("%v:__VERIFIER_atomic_begin();") loc [("__VERIFIER_atomic_begin", Fv atomicBegin)]) in let aend = (cInstr ("%v:__VERIFIER_atomic_end();") loc [("__VERIFIER_atomic_end", Fv atomicEnd)]) in diff --git a/src/util/backtrace/goblint_backtrace.ml b/src/util/backtrace/goblint_backtrace.ml index 29198c27ea..513753bddb 100644 --- a/src/util/backtrace/goblint_backtrace.ml +++ b/src/util/backtrace/goblint_backtrace.ml @@ -36,6 +36,15 @@ let protect ~(mark: unit -> mark) ~(finally: unit -> unit) work = add_mark work_exn (mark ()); Printexc.raise_with_backtrace work_exn work_bt +(* Copied & modified from protect. *) +let wrap_val ~(mark:mark) work = + try + work () + with work_exn -> + let work_bt = Printexc.get_raw_backtrace () in + add_mark work_exn mark; + Printexc.raise_with_backtrace work_exn work_bt + let mark_printers: (mark -> string option) list ref = ref [] diff --git a/src/util/backtrace/goblint_backtrace.mli b/src/util/backtrace/goblint_backtrace.mli index e2b6ed2913..e53bfd826a 100644 --- a/src/util/backtrace/goblint_backtrace.mli +++ b/src/util/backtrace/goblint_backtrace.mli @@ -24,6 +24,9 @@ val add_mark: exn -> mark -> unit val protect: mark:(unit -> mark) -> finally:(unit -> unit) -> (unit -> 'a) -> 'a (** {!Fun.protect} with additional [~mark] addition to all exceptions. *) +val wrap_val: mark:mark -> (unit -> 'a) -> 'a +(** {!protect} with [~mark] value and without [~finally]. *) + val print_marktrace: out_channel -> exn -> unit (** Print trace of marks of an exception. diff --git a/src/util/library/libraryFunctions.ml b/src/util/library/libraryFunctions.ml index 689eb17126..979876729d 100644 --- a/src/util/library/libraryFunctions.ml +++ b/src/util/library/libraryFunctions.ml @@ -722,7 +722,7 @@ let linux_userspace_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[ ("__errno", unknown []); ("__errno_location", unknown []); ("__h_errno_location", unknown []); - ("__printf_chk", unknown [drop "flag" []; drop "format" [r]]); + ("__printf_chk", unknown (drop "flag" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("__fprintf_chk", unknown (drop "stream" [r_deep; w_deep] :: drop "flag" [] :: drop "format" [r] :: VarArgs (drop' [r]))); ("__vfprintf_chk", unknown [drop "stream" [r_deep; w_deep]; drop "flag" []; drop "format" [r]; drop "ap" [r_deep]]); ("sysinfo", unknown [drop "info" [w_deep]]); diff --git a/src/util/loopUnrolling.ml b/src/util/loopUnrolling.ml index e433c34b4a..506337ac1b 100644 --- a/src/util/loopUnrolling.ml +++ b/src/util/loopUnrolling.ml @@ -68,7 +68,7 @@ class isPointedAtVisitor(var) = object inherit nopCilVisitor method! vexpr = function - | AddrOf (Var info, NoOffset) when info.vid == var.vid -> raise Found + | AddrOf (Var info, NoOffset) when CilType.Varinfo.equal info var -> raise Found | _ -> DoChildren end @@ -76,7 +76,7 @@ class hasAssignmentVisitor(var) = object inherit nopCilVisitor method! vinst = function - | Set ((Var info, NoOffset),_,_,_) when info.vid == var.vid -> raise Found + | Set ((Var info, NoOffset),_,_,_) when CilType.Varinfo.equal info var -> raise Found | _ -> SkipChildren end @@ -119,6 +119,20 @@ class findAssignmentConstDiff((diff: Z.t option ref), var) = object | _ -> SkipChildren end +class findStmtContainsInstructions = object + inherit nopCilVisitor + method! vinst = function + | Set _ + | Call _ -> raise Found + | _ -> DoChildren +end + +let containsInstructions stmt = + try + ignore @@ visitCilStmt (new findStmtContainsInstructions) stmt; false + with Found -> + true + let isCompare = function | Lt | Gt | Le | Ge | Ne -> true (*an loop that test for equality can not be of the type we look for*) | _ -> false @@ -154,7 +168,16 @@ let constBefore var loop f = let targetLocation = loopLocation loop in let rec lastAssignmentToVarBeforeLoop (current: (Z.t option)) (statements: stmt list) = match statements with | st::stmts -> ( - let current' = if st.labels <> [] then (Logs.debug "has Label"; (None)) else current in + let current' = + (* If there exists labels that are not the ones inserted by loop unrolling, forget the found assigned constant value *) + if List.exists (function + | Label (s,_,_) -> not (String.starts_with ~prefix:"loop_continue" s || String.starts_with ~prefix:"loop_end" s) + | _ -> true) st.labels + then + (Logs.debug "has Label"; (None)) + else + current + in match st.skind with | Instr list -> ( match lastAssignToVar var list with @@ -210,89 +233,105 @@ let constBefore var loop f = in fst @@ lastAssignmentToVarBeforeLoop (Some Z.zero) f.sbody.bstmts (*the top level call should never return false*) -let rec loopIterations start diff comp = - let flip = function - | Lt -> Gt - | Gt -> Lt - | Ge -> Le - | Le -> Ge - | s -> s - in let loopIterations' goal shouldBeExact = - let range = Z.sub goal start in - if Z.equal diff Z.zero || Z.equal range Z.zero || (Z.gt diff Z.zero && Z.lt range Z.zero) || (Z.lt diff Z.zero && Z.gt range Z.zero) then - None (*unfitting parameters*) - else ( - let roundedDown = Z.div range diff in - let isExact = Z.equal (Z.mul roundedDown diff) range in - if isExact then - Some roundedDown - else if shouldBeExact then - None - else - Some (Z.succ roundedDown) - ) - in - match comp with - | BinOp (op, (Const _ as c), var, t) -> loopIterations start diff (BinOp (flip op, var, c, t)) - | BinOp (Lt, _, (Const (CInt (cint,_,_) )), _) -> if Z.lt diff Z.zero then None else loopIterations' cint false - | BinOp (Gt, _, (Const (CInt (cint,_,_) )), _) -> if Z.gt diff Z.zero then None else loopIterations' cint false - | BinOp (Le, _, (Const (CInt (cint,_,_) )), _) -> if Z.lt diff Z.zero then None else loopIterations' (Z.succ cint) false - | BinOp (Ge, _, (Const (CInt (cint,_,_) )), _) -> if Z.gt diff Z.zero then None else loopIterations' (Z.pred cint) false - | BinOp (Ne, _, (Const (CInt (cint,_,_) )), _) -> loopIterations' cint true +(*find a single break in the else branch of a toplevel if*) +let findBreakComparison loopStatement = + try + let compOption = ref None in + let visitor = new findBreakVisitor (compOption) in + ignore @@ visitCilBlock visitor (loopBody loopStatement); + !compOption + with WrongOrMultiple -> + None + +let findLoopVarAndGoal loopStatement func (op, exp1, exp2) = + match Cil.stripCasts exp1, Cil.stripCasts exp2 with + | Const (CInt (goal, _, _)), Lval (Var varinfo, NoOffset) when not varinfo.vglob -> + (* TODO: define isCompare and flip in cilfacade and refactor to use instead of the many separately defined similar functions *) + let flip = function | Lt -> Gt | Gt -> Lt | Ge -> Le | Le -> Ge | s -> s in + Some (flip op, varinfo, goal) + | Lval (Var varinfo, NoOffset), Const (CInt (goal, _, _)) when not varinfo.vglob -> + Some (op, varinfo, goal) + | Lval (Var varinfo, NoOffset), Lval (Var varinfo2, NoOffset) when not varinfo.vglob && not varinfo2.vglob -> + (* When loop condition has a comparison between variables, we assume that the left one is the counter and right one is the bound. + TODO: can we do something more meaningful instead of this assumption? *) + begin match constBefore varinfo2 loopStatement func with + | Some goal -> Logs.debug "const: %a %a" CilType.Varinfo.pretty varinfo2 GobZ.pretty goal; Some (op, varinfo, goal) + | None -> None + end; + | _ -> None + +let getLoopVar loopStatement func = function + | BinOp (op, exp1, exp2, TInt _) when isCompare op -> findLoopVarAndGoal loopStatement func (op, exp1, exp2) + | _ -> None + +let getsPointedAt var func = + try + let visitor = new isPointedAtVisitor (var) in + ignore @@ visitCilFunction visitor func; + false + with Found -> + true + +let assignmentDifference loop var = + try + let diff = ref None in + let visitor = new findAssignmentConstDiff (diff, var) in + ignore @@ visitCilBlock visitor loop; + !diff + with WrongOrMultiple -> + None + +let adjustGoal diff goal op = + match op with + | Lt -> if Z.lt diff Z.zero then None else Some goal + | Gt -> if Z.gt diff Z.zero then None else Some goal + | Le -> if Z.lt diff Z.zero then None else Some (Z.succ goal) + | Ge -> if Z.gt diff Z.zero then None else Some (Z.pred goal) + | Ne -> Some goal | _ -> failwith "unexpected comparison in loopIterations" -let ( >>= ) = Option.bind -let fixedLoopSize loopStatement func = - let findBreakComparison = try (*find a single break in the else branch of a toplevel if*) - let compOption = ref None in - let visitor = new findBreakVisitor(compOption) in - ignore @@ visitCilBlock visitor (loopBody loopStatement); - !compOption - with | WrongOrMultiple -> None - in let getLoopVar = function - | BinOp (op, (Const (CInt _ )), Lval ((Var info), NoOffset), (TInt _)) - | BinOp (op, Lval ((Var info), NoOffset), (Const (CInt _ )), (TInt _)) when isCompare op && not info.vglob-> - Some info - | _ -> None - in let getsPointedAt var = try - let visitor = new isPointedAtVisitor(var) in - ignore @@ visitCilFunction visitor func; - false - with | Found -> true - in let assignmentDifference loop var = try - let diff = ref None in - let visitor = new findAssignmentConstDiff(diff, var) in - ignore @@ visitCilStmt visitor loop; - !diff - with | WrongOrMultiple -> None - in +let loopIterations start diff goal shouldBeExact = + let range = Z.sub goal start in + if Z.equal diff Z.zero || Z.equal range Z.zero || (Z.gt diff Z.zero && Z.lt range Z.zero) || (Z.lt diff Z.zero && Z.gt range Z.zero) then + None (*unfitting parameters*) + else ( + let roundedDown = Z.div range diff in + let isExact = Z.equal (Z.mul roundedDown diff) range in + if isExact then + Some roundedDown + else if shouldBeExact then + None + else + Some (Z.succ roundedDown) + ) - findBreakComparison >>= fun comparison -> - getLoopVar comparison >>= fun var -> - if getsPointedAt var then +let fixedLoopSize loopStatement func = + let open GobOption.Syntax in + let* comparison = findBreakComparison loopStatement in + let* op, var, goal = getLoopVar loopStatement func comparison in + if getsPointedAt var func then None else - constBefore var loopStatement func >>= fun start -> - assignmentDifference loopStatement var >>= fun diff -> - Logs.debug "comparison: "; - Pretty.fprint stderr (dn_exp () comparison) ~width:max_int; - Logs.debug ""; - Logs.debug "variable: "; - Logs.debug "%s" var.vname; - Logs.debug "start:"; - Logs.debug "%s" @@ Z.to_string start; - Logs.debug "diff:"; - Logs.debug "%s" @@ Z.to_string diff; - let iterations = loopIterations start diff comparison in + let diff = Option.value (assignmentDifference (loopBody loopStatement) var) ~default:Z.one in + (* Assume var start value if there was no constant assignment to the var before loop; + Assume it to be 0, if loop is increasing and 11 (TODO: can we do better than just 11?) if loop is decreasing *) + let start = Option.value (constBefore var loopStatement func) ~default:(if diff < Z.zero then Z.of_int 11 else Z.zero) in + let* goal = adjustGoal diff goal op in + let iterations = loopIterations start diff goal (op=Ne) in + Logs.debug "comparison: %a" CilType.Exp.pretty comparison; + Logs.debug "variable: %s" var.vname; + Logs.debug "start: %a" GobZ.pretty start; + Logs.debug "diff: %a" GobZ.pretty diff; match iterations with | None -> Logs.debug "iterations failed"; None | Some s -> try let s' = Z.to_int s in - Logs.debug "iterations:"; - Logs.debug "%d" s'; + Logs.debug "iterations: %d" s'; Some s' - with Z.Overflow -> Logs.debug "iterations too big for integer"; None + with Z.Overflow -> + Logs.debug "iterations too big for integer"; + None class arrayVisitor = object @@ -305,61 +344,26 @@ class arrayVisitor = object end let annotateArrays loopBody = ignore @@ visitCilBlock (new arrayVisitor) loopBody -(*unroll loops that handle locks, threads and mallocs, asserts and reach_error*) -class loopUnrollingCallVisitor = object - inherit nopCilVisitor - - method! vinst = function - | Call (_,Lval ((Var info), NoOffset),args,_,_) when LibraryFunctions.is_special info -> ( - let desc = LibraryFunctions.find info in - match desc.special args with - | Malloc _ - | Calloc _ - | Realloc _ - | Alloca _ - | Lock _ - | Unlock _ - | ThreadCreate _ - | Assert _ - | Bounded _ - | ThreadJoin _ -> - raise Found; - | _ -> - if List.mem "specification" @@ get_string_list "ana.autotune.activated" && get_string "ana.specification" <> "" then ( - if Svcomp.is_error_function' info (SvcompSpec.of_option ()) then - raise Found - ); - DoChildren - ) - | _ -> DoChildren - -end +let max_default_unrolls_per_spec (spec: Svcomp.Specification.t) = + match spec with + | NoDataRace -> 0 + | NoOverflow -> 2 + | _ -> 4 let loop_unrolling_factor loopStatement func totalLoops = let configFactor = get_int "exp.unrolling-factor" in - if AutoTune0.isActivated "loopUnrollHeuristic" then - let unrollFunctionCalled = try - let thisVisitor = new loopUnrollingCallVisitor in - ignore (visitCilStmt thisVisitor loopStatement); - false; - with - Found -> true - in - (*unroll up to near an instruction count, higher if the loop uses malloc/lock/threads *) - let targetInstructions = if unrollFunctionCalled then 50 else 25 in - let loopStats = AutoTune0.collectFactors visitCilStmt loopStatement in - if loopStats.instructions > 0 then - let fixedLoop = fixedLoopSize loopStatement func in - (* Unroll at least 10 times if there are only few (17?) loops *) - let unroll_min = if totalLoops < 17 && AutoTune0.isActivated "forceLoopUnrollForFewLoops" then 10 else 0 in - match fixedLoop with - | Some i -> if i * loopStats.instructions < 100 then (Logs.debug "fixed loop size"; i) else max unroll_min (100 / loopStats.instructions) - | _ -> max unroll_min (targetInstructions / loopStats.instructions) + if containsInstructions loopStatement then + if AutoTune0.isActivated "loopUnrollHeuristic" then + match fixedLoopSize loopStatement func with + | Some i when i <= 20 -> Logs.debug "fixed loop size %d" i; i + | _ -> + match Svcomp.Specification.of_option () with + | [] -> 4 + | specs -> BatList.max @@ List.map max_default_unrolls_per_spec specs else - (* Don't unroll empty (= while(1){}) loops*) - 0 - else - configFactor + configFactor + else (* Don't unroll empty (= while(1){}) loops*) + 0 (*actual loop unrolling*) @@ -438,16 +442,19 @@ let copy_and_patch_labels break_target current_continue_target stmts = let patchLabelsVisitor = new patchLabelsGotosVisitor(StatementHashTable.find_opt gotos) in List.map (visitCilStmt patchLabelsVisitor) stmts' -class loopUnrollingVisitor(func, totalLoops) = object +class loopUnrollingVisitor (func, totalLoops) = object (* Labels are simply handled by giving them a fresh name. Jumps coming from outside will still always go to the original label! *) inherit nopCilVisitor - method! vstmt s = - let duplicate_and_rem_labels s = - match s.skind with - | Loop (b,loc, loc2, break , continue) -> - let factor = loop_unrolling_factor s func totalLoops in - if(factor > 0) then ( + val mutable nests = 0 + + method! vstmt stmt = + let duplicate_and_rem_labels stmt = + match stmt.skind with + | Loop (b, loc, loc2, break, continue) -> + nests <- nests - 1; Logs.debug "nests: %i" nests; + let factor = loop_unrolling_factor stmt func totalLoops in + if factor > 0 then ( Logs.info "unrolling loop at %a with factor %d" CilType.Location.pretty loc factor; annotateArrays b; (* top-level breaks should immediately go to the end of the loop, and not just break out of the current iteration *) @@ -459,14 +466,19 @@ class loopUnrollingVisitor(func, totalLoops) = object one_copy_stmts @ [current_continue_target] ) in - mkStmt (Block (mkBlock (List.flatten copies @ [s; break_target]))) - ) else s (*no change*) - | _ -> s + mkStmt (Block (mkBlock (List.flatten copies @ [stmt; break_target]))) + ) else stmt (*no change*) + | _ -> stmt in - ChangeDoChildrenPost(s, duplicate_and_rem_labels) + match stmt.skind with + | Loop _ when nests + 1 < 4 -> nests <- nests + 1; ChangeDoChildrenPost(stmt, duplicate_and_rem_labels) + | Loop _ -> SkipChildren + | _ -> ChangeDoChildrenPost(stmt, duplicate_and_rem_labels) end let unroll_loops fd totalLoops = - Cil.populateLabelAlphaTable fd; - let thisVisitor = new loopUnrollingVisitor(fd, totalLoops) in - ignore (visitCilFunction thisVisitor fd) + if not (Cil.hasAttribute "goblint_stub" fd.svar.vattr) then ( + Cil.populateLabelAlphaTable fd; + let thisVisitor = new loopUnrollingVisitor(fd, totalLoops) in + ignore (visitCilFunction thisVisitor fd) + ) diff --git a/src/util/server.ml b/src/util/server.ml index 7b603e7c6e..1bdc3e1ea9 100644 --- a/src/util/server.ml +++ b/src/util/server.ml @@ -320,6 +320,7 @@ let () = let process { reset } serve = try analyze serve ~reset; + (* TODO: generalize VerifyError for AnalysisState.unsound_both_branches_dead *) {status = if !AnalysisState.verified = Some false then VerifyError else Success} with | Sys.Break -> diff --git a/src/vendor/ppx_blob/LICENSE.txt b/src/vendor/ppx_blob/LICENSE.txt deleted file mode 100644 index 00d2e135a7..0000000000 --- a/src/vendor/ppx_blob/LICENSE.txt +++ /dev/null @@ -1,24 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to \ No newline at end of file diff --git a/src/vendor/ppx_blob/README.md b/src/vendor/ppx_blob/README.md deleted file mode 100644 index fdd7448a32..0000000000 --- a/src/vendor/ppx_blob/README.md +++ /dev/null @@ -1 +0,0 @@ -ppx_blob fix for `(lang dune 3.0)` vendored from . diff --git a/src/vendor/ppx_blob/src/dune b/src/vendor/ppx_blob/src/dune deleted file mode 100644 index 112b1d07c0..0000000000 --- a/src/vendor/ppx_blob/src/dune +++ /dev/null @@ -1,5 +0,0 @@ -(library - (name ppx_blob) - ; (public_name ppx_blob) - (kind ppx_rewriter) - (libraries ppxlib)) \ No newline at end of file diff --git a/src/vendor/ppx_blob/src/ppx_blob.ml b/src/vendor/ppx_blob/src/ppx_blob.ml deleted file mode 100644 index 622a99f8f6..0000000000 --- a/src/vendor/ppx_blob/src/ppx_blob.ml +++ /dev/null @@ -1,35 +0,0 @@ -open Ppxlib - -let location_errorf ~loc = - Format.ksprintf (fun err -> - raise (Ocaml_common.Location.Error (Ocaml_common.Location.error ~loc err)) - ) - -let find_file_path ~loc file_name = - let dirname = loc.Ocaml_common.Location.loc_start.pos_fname |> Filename.dirname in - let relative_path = Filename.concat dirname file_name in - List.find Sys.file_exists [relative_path; file_name] - -let get_blob ~loc file_name = - try - let file_path = find_file_path ~loc file_name in - let c = open_in_bin file_path in - let s = String.init (in_channel_length c) (fun _ -> input_char c) in - close_in c; - s - with _ -> - location_errorf ~loc "[%%blob] could not find or load file %s" file_name - -let expand ~ctxt file_name = - let loc = Expansion_context.Extension.extension_point_loc ctxt in - Ast_builder.Default.estring ~loc (get_blob ~loc file_name) - -let extension = - Extension.V3.declare "blob" Extension.Context.expression - Ast_pattern.(single_expr_payload (estring __)) - expand - -let rule = Ppxlib.Context_free.Rule.extension extension - -;; -Driver.register_transformation ~rules:[rule] "ppx_blob" \ No newline at end of file diff --git a/src/vendor/ppx_easy_deriving/LICENSE.md b/src/vendor/ppx_easy_deriving/LICENSE.md new file mode 100644 index 0000000000..ae6337b11b --- /dev/null +++ b/src/vendor/ppx_easy_deriving/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Simmo Saan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/vendor/ppx_easy_deriving/README.md b/src/vendor/ppx_easy_deriving/README.md new file mode 100644 index 0000000000..99a5e7e140 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/README.md @@ -0,0 +1,4 @@ +# ppx_easy_deriving + +Goblint vendors a subset of the unreleased [ppx_easy_deriving](https://github.com/sim642/ppx_easy_deriving) library. +It only includes products, excluding simples and variants. diff --git a/src/vendor/ppx_easy_deriving/deriver.ml b/src/vendor/ppx_easy_deriving/deriver.ml new file mode 100644 index 0000000000..533a77959c --- /dev/null +++ b/src/vendor/ppx_easy_deriving/deriver.ml @@ -0,0 +1,100 @@ +open Ppxlib +open Ast_builder.Default + +include Deriver_intf + +module Make (Arg: Intf.S): S = +struct + let attr = Attribute.declare (Printf.sprintf "deriving.%s.%s" Arg.name Arg.name) Attribute.Context.core_type Ast_pattern.(single_expr_payload __) (fun expr -> expr) + + let unit ~loc = Arg.tuple ~loc [] + + let rec expr ~loc ~quoter ct = + match Attribute.get attr ct with + | Some expr -> + Expansion_helpers.Quoter.quote quoter expr + | None -> + match ct with + | [%type: unit] -> + unit ~loc + | {ptyp_desc = Ptyp_constr ({txt = lid; loc}, args); _} -> + let ident = pexp_ident ~loc {loc; txt = Expansion_helpers.mangle_lid (Prefix Arg.name) lid} in + let ident = Expansion_helpers.Quoter.quote quoter ident in + let apply_args = List.map (fun ct -> + (Nolabel, expr ~loc ~quoter ct) + ) args + in + pexp_apply ~loc ident apply_args + | {ptyp_desc = Ptyp_tuple elems; _} -> + expr_tuple ~loc ~quoter elems + | {ptyp_desc = Ptyp_var name; _} -> + evar ~loc ("poly_" ^ name) + | _ -> + pexp_extension ~loc (Location.error_extensionf ~loc "unsupported core type") + + and expr_record ~loc ~quoter (lds: label_declaration list) = + let les = List.map (fun {pld_name = {txt = label; _}; pld_type; _} -> + (Lident label, expr ~loc ~quoter pld_type) + ) lds + in + Arg.record ~loc les + + and expr_tuple ~loc ~quoter elems = + let es = List.map (expr ~loc ~quoter) elems in + Arg.tuple ~loc es + + let expr_declaration ~loc ~quoter = function + | {ptype_kind = Ptype_abstract; ptype_manifest = Some ct; _} -> + expr ~loc ~quoter ct + | {ptype_kind = Ptype_abstract; _} -> + pexp_extension ~loc (Location.error_extensionf ~loc "unsupported abstract type") + | {ptype_kind = Ptype_variant constrs; _} -> + pexp_extension ~loc (Location.error_extensionf ~loc "unsupported variant type") + | {ptype_kind = Ptype_open; _} -> + pexp_extension ~loc (Location.error_extensionf ~loc "unsupported open type") + | {ptype_kind = Ptype_record fields; _} -> + expr_record ~loc ~quoter fields + + let typ ~loc td = + let ct = Ppx_deriving.core_type_of_type_decl td in + Ppx_deriving.poly_arrow_of_type_decl + (Arg.typ ~loc) + td + (Arg.typ ~loc ct) + + let generate_impl ~ctxt (_rec_flag, type_declarations) = + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + let vbs = List.map (fun td -> + let quoter = Expansion_helpers.Quoter.create () in + let expr = expr_declaration ~loc ~quoter td in + let expr = Expansion_helpers.Quoter.sanitize quoter expr in + let expr = Ppx_deriving.poly_fun_of_type_decl td expr in + let ct = typ ~loc td in + let pat = ppat_var ~loc {loc; txt = Expansion_helpers.mangle_type_decl (Prefix Arg.name) td} in + let pat = ppat_constraint ~loc pat ct in + Ast_helper.Vb.mk ~loc ~attrs:[Ppx_deriving.attr_warning [%expr "-39"]] pat expr + ) type_declarations + in + [Ast_helper.Str.value ~loc Recursive vbs] + + let generate_intf ~ctxt (_rec_flag, type_declarations) = + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + List.map (fun td -> + let ct = typ ~loc td in + let val_ = Ast_helper.Val.mk ~loc {loc; txt = Expansion_helpers.mangle_type_decl (Prefix Arg.name) td} ct in + Ast_helper.Sig.value ~loc val_ + ) type_declarations + + let impl_generator = Deriving.Generator.V2.make_noarg generate_impl + let intf_generator = Deriving.Generator.V2.make_noarg generate_intf + let extension ~loc ~path:_ ct = + let quoter = Expansion_helpers.Quoter.create () in + let expr = expr ~loc ~quoter ct in + Expansion_helpers.Quoter.sanitize quoter expr + + let register () = + Deriving.add Arg.name + ~sig_type_decl:intf_generator + ~str_type_decl:impl_generator + ~extension +end diff --git a/src/vendor/ppx_easy_deriving/deriver.mli b/src/vendor/ppx_easy_deriving/deriver.mli new file mode 100644 index 0000000000..a8238f6f6f --- /dev/null +++ b/src/vendor/ppx_easy_deriving/deriver.mli @@ -0,0 +1,3 @@ +(** Registerable deriver. *) + +include Deriver_intf.Deriver (** @inline *) diff --git a/src/vendor/ppx_easy_deriving/deriver_intf.ml b/src/vendor/ppx_easy_deriving/deriver_intf.ml new file mode 100644 index 0000000000..565a6aef38 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/deriver_intf.ml @@ -0,0 +1,13 @@ +module type S = +sig + val register: unit -> Ppxlib.Deriving.t + (** Register deriver with ppxlb. *) +end + +module type Deriver = +sig + module type S = S + + module Make (_: Intf.S): S + (** Make registerable deriver. *) +end diff --git a/src/vendor/ppx_easy_deriving/dune b/src/vendor/ppx_easy_deriving/dune new file mode 100644 index 0000000000..7d50a56e43 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/dune @@ -0,0 +1,4 @@ +(library + (name ppx_easy_deriving) + (libraries ppxlib ppx_deriving.api) + (preprocess (pps ppxlib.metaquot))) diff --git a/src/vendor/ppx_easy_deriving/intf.ml b/src/vendor/ppx_easy_deriving/intf.ml new file mode 100644 index 0000000000..b30c7c36a0 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/intf.ml @@ -0,0 +1,64 @@ +(** Main interfaces. *) + +open Ppxlib + +(** Deriver name interface. *) +module type Name = +sig + val name: string + (** Deriver name. + + For example, with the name "equal": + + Use [[@@deriving equal]] after a type definition. + + The derived value/function is [val equal: ...] (if the type is named [t]) or [val equal_ty: ...] (otherwise if the type is named [ty]). + + Use [[@equal ...]] after a type expression to override the underlying value/function used for it. + + Use [[%equal: ty]] as an expression for the value/function of type [ty]. *) +end + +(** Deriver base interface. *) +module type Base = +sig + include Name + val typ: loc:location -> core_type -> core_type + (** Derived value/function type for a given type. + + For example, "equal" deriver would map [t] to [t -> t -> bool]. *) +end + +module Tuple = +struct + + (** Tuple deriver interface. *) + module type S = + sig + include Base + val tuple: loc:location -> expression list -> expression + (** Compose derived values/functions for tuple elements into derived value/function for the tuple. *) + end +end + +module Record = +struct + + (** Record deriver interface. *) + module type S = + sig + include Base + val record: loc:location -> (longident * expression) list -> expression + (** Compose derived values/functions for record fields into derived value/function for the record. *) + end +end + +module Full = +struct + + (** Full deriver interface. *) + module type S = + sig + include Tuple.S + include Record.S + end +end + +module type S = Full.S +(** Deriver interface. *) diff --git a/src/vendor/ppx_easy_deriving/pat_exp.ml b/src/vendor/ppx_easy_deriving/pat_exp.ml new file mode 100644 index 0000000000..a9d43d07e6 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/pat_exp.ml @@ -0,0 +1,46 @@ +open Ppxlib +open Ast_builder.Default + +type t = + | Record of (longident * t) list + | Tuple of t list + | Unit + | Base of string +let create_record ~prefix ls = + Record (List.mapi (fun i l -> (l, Base (prefix ^ string_of_int (i + 1)))) ls) +let create_tuple ~prefix n = + match n with + | 0 -> Unit + | 1 -> Base (prefix ^ "1") + | n -> Tuple (List.init n (fun i -> Base (prefix ^ string_of_int (i + 1)))) +let rec to_pat ~loc = function + | Record xs -> + ppat_record ~loc (List.map (fun (l, x) -> + (Located.mk ~loc l, to_pat ~loc x) + ) xs) Closed + | Tuple xs -> + ppat_tuple ~loc (List.map (to_pat ~loc) xs) + | Unit -> + [%pat? ()] + | Base s -> + ppat_var ~loc (Located.mk ~loc s) +let rec to_exps ~loc = function + | Record xs -> + List.flatten (List.map (fun (_, x) -> to_exps ~loc x) xs) + | Tuple xs -> + List.flatten (List.map (to_exps ~loc) xs) + | Unit -> + [] + | Base s -> + [pexp_ident ~loc {loc; txt = Lident s}] +let rec to_exp ~loc = function + | Record xs -> + pexp_record ~loc (List.map (fun (l, x) -> + (Located.mk ~loc l, to_exp ~loc x) + ) xs) None + | Tuple xs -> + pexp_tuple ~loc (List.map (to_exp ~loc) xs) + | Unit -> + [%expr ()] + | Base s -> + pexp_ident ~loc {loc; txt = Lident s} diff --git a/src/vendor/ppx_easy_deriving/pat_exp.mli b/src/vendor/ppx_easy_deriving/pat_exp.mli new file mode 100644 index 0000000000..a4878afa81 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/pat_exp.mli @@ -0,0 +1,16 @@ +(** Common representation of patterns and expressions. *) + +open Ppxlib + +type t = + | Record of (longident * t) list + | Tuple of t list + | Unit + | Base of string + +val create_record: prefix:string -> longident list -> t +val create_tuple: prefix:string -> int -> t + +val to_pat: loc:location -> t -> pattern +val to_exps: loc:location -> t -> expression list +val to_exp: loc:location -> t -> expression diff --git a/src/vendor/ppx_easy_deriving/ppx_easy_deriving.ml b/src/vendor/ppx_easy_deriving/ppx_easy_deriving.ml new file mode 100644 index 0000000000..a490f33557 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/ppx_easy_deriving.ml @@ -0,0 +1,15 @@ +(** Library for easily defining PPX derivers without boilerplate and runtime overhead. *) + +(** {1 Interfaces} *) + +include Intf (** @inline *) + + +(** {1 Deriver} *) + +module Deriver = Deriver + + +(** {1 Easier constructs} *) + +module Product = Product diff --git a/src/vendor/ppx_easy_deriving/product.ml b/src/vendor/ppx_easy_deriving/product.ml new file mode 100644 index 0000000000..2135552b63 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/product.ml @@ -0,0 +1,226 @@ +open Ppxlib +open Ast_builder.Default + +include Product_intf + +module type Product_S = S + +module Make (P: S): Intf.S = +struct + include P + + let tuple ~loc es = + let n = List.length es in + let pe_create = Pat_exp.create_tuple n in + P.product ~loc ~pe_create es + + let record ~loc les = + let ls = List.map fst les in + let pe_create = Pat_exp.create_record ls in + let es = List.map snd les in + P.product ~loc ~pe_create es +end + +module Reduce = +struct + include Reduce + + module Conjunctive = + struct + include Conjunctive + + module Make (C: S): Reduce.S = + struct + let name = C.name + let typ ~loc _ = [%type: bool] + let unit ~loc = [%expr true] + let both ~loc e1 e2 = [%expr [%e e1] && [%e e2]] + end + end +end + +module Reduce1 = +struct + include Reduce1 + + module Make (R1: S): Intf.S = + struct + module P: Product_S = + struct + let name = R1.name + let typ ~loc t = [%type: [%t t] -> [%t R1.typ ~loc t]] + + let product ~loc ~pe_create es = + let pe = pe_create ~prefix:"x" in + let body = + let es = List.map2 (fun e x -> + [%expr [%e e] [%e x]] + ) es (Pat_exp.to_exps ~loc pe) + in + Util.reduce ~unit:(R1.unit ~loc) ~both:(R1.both ~loc) es + in + [%expr fun [%p Pat_exp.to_pat ~loc pe] -> [%e body]] + end + + include Make (P) + end +end + +module Reduce2 = +struct + include Reduce2 + + module Make (R2: S): Intf.S = + struct + module P: Product_S = + struct + let name = R2.name + let typ ~loc t = [%type: [%t t] -> [%t t] -> [%t R2.typ ~loc t]] + + let product ~loc ~pe_create es = + let pel = pe_create ~prefix:"l" in + let per = pe_create ~prefix:"r" in + let body = + let esl = Pat_exp.to_exps ~loc pel in + let esr = Pat_exp.to_exps ~loc per in + let es = Util.map3 (fun e l r -> + [%expr [%e e] [%e l] [%e r]] + ) es esl esr + in + Util.reduce ~unit:(R2.unit ~loc) ~both:(R2.both ~loc) es + in + let pl = Pat_exp.to_pat ~loc pel in + let pr = Pat_exp.to_pat ~loc per in + [%expr fun [%p pl] [%p pr] -> [%e body]] + end + + include Make (P) + end +end + +module Create = +struct + include Create + + module Make (C: S): Intf.S = + struct + let name = C.name + let typ ~loc t = [%type: [%t C.typ ~loc t] -> [%t t]] + + let tuple ~loc es = + match es with + | [] -> [%expr fun _ -> ()] + | [e] -> e + | _ :: _ -> + let elems = List.map (fun e -> + [%expr [%e e] x] + ) es + in + let body = pexp_tuple ~loc elems in + [%expr fun x -> [%e body]] + + let record ~loc les = + let fields = List.map (fun (l, e) -> + (Located.mk ~loc l, [%expr [%e e] x]) + ) les + in + let body = pexp_record ~loc fields None in + [%expr fun x -> [%e body]] + end +end + +module Map1 = +struct + include Map1 + + module Make (M1: S): Intf.S = + struct + let name = M1.name + let typ ~loc t = [%type: [%t t] -> [%t t]] + + let tuple ~loc es = + let n = List.length es in + let pe = Pat_exp.create_tuple ~prefix:"x" n in + let elems = + List.map2 (fun e x -> + [%expr [%e e] [%e x]] + ) es (Pat_exp.to_exps ~loc pe) + in + let body = + match elems with + | [] -> [%expr ()] + | [elem] -> elem + | _ :: _ -> pexp_tuple ~loc elems + in + [%expr fun [%p Pat_exp.to_pat ~loc pe] -> [%e body]] + + let record ~loc les = + let ls = List.map fst les in + let pe = Pat_exp.create_record ~prefix:"x" ls in + let es = List.map snd les in + let elems = + List.map2 (fun e x -> + [%expr [%e e] [%e x]] + ) es (Pat_exp.to_exps ~loc pe) + in + let fields = List.map2 (fun l elem -> + (Located.mk ~loc l, elem) + ) ls elems + in + let body = pexp_record ~loc fields None in + [%expr fun [%p Pat_exp.to_pat ~loc pe] -> [%e body]] + end +end + +module Map2 = +struct + include Map2 + + module Make (M2: S): Intf.S = + struct + let name = M2.name + let typ ~loc t = [%type: [%t t] -> [%t t] -> [%t t]] + + let tuple ~loc es = + let n = List.length es in + let pel = Pat_exp.create_tuple ~prefix:"l" n in + let per = Pat_exp.create_tuple ~prefix:"r" n in + let elems = + let esl = Pat_exp.to_exps ~loc pel in + let esr = Pat_exp.to_exps ~loc per in + Util.map3 (fun e l r -> + [%expr [%e e] [%e l] [%e r]] + ) es esl esr + in + let body = + match elems with + | [] -> [%expr ()] + | [elem] -> elem + | _ :: _ -> pexp_tuple ~loc elems + in + let pl = Pat_exp.to_pat ~loc pel in + let pr = Pat_exp.to_pat ~loc per in + [%expr fun [%p pl] [%p pr] -> [%e body]] + + let record ~loc les = + let ls = List.map fst les in + let pel = Pat_exp.create_record ~prefix:"l" ls in + let per = Pat_exp.create_record ~prefix:"r" ls in + let es = List.map snd les in + let elems = + let esl = Pat_exp.to_exps ~loc pel in + let esr = Pat_exp.to_exps ~loc per in + Util.map3 (fun e l r -> + [%expr [%e e] [%e l] [%e r]] + ) es esl esr + in + let fields = List.map2 (fun l elem -> + (Located.mk ~loc l, elem) + ) ls elems + in + let body = pexp_record ~loc fields None in + let pl = Pat_exp.to_pat ~loc pel in + let pr = Pat_exp.to_pat ~loc per in + [%expr fun [%p pl] [%p pr] -> [%e body]] + end +end diff --git a/src/vendor/ppx_easy_deriving/product.mli b/src/vendor/ppx_easy_deriving/product.mli new file mode 100644 index 0000000000..d0b45f106c --- /dev/null +++ b/src/vendor/ppx_easy_deriving/product.mli @@ -0,0 +1,3 @@ +(** Product derivers which unify tuple and record derivers. *) + +include Product_intf.Product (** @inline *) diff --git a/src/vendor/ppx_easy_deriving/product_intf.ml b/src/vendor/ppx_easy_deriving/product_intf.ml new file mode 100644 index 0000000000..14964bbee7 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/product_intf.ml @@ -0,0 +1,132 @@ +open Ppxlib + +module type S = +sig + include Intf.Base + val product: loc:location -> pe_create:(prefix:string -> Pat_exp.t) -> expression list -> expression + (** Compose derived values/functions for product elements into derived value/function for the product. + + @param pe_create factory for patterns/expressions of the actual type. *) +end + +module Reduce = +struct + module type S = + sig + include Intf.Base + val unit: loc:location -> expression + (** Derived value/function for [unit] type. *) + + val both: loc:location -> expression -> expression -> expression + (** Compose derived values/functions in a product into derived value/function for the pair. *) + end + + module Conjunctive = + struct + module type S = Intf.Name + end +end + +module Reduce1 = +struct + module type S = Reduce.S +end + +module Reduce2 = +struct + module type S = Reduce.S +end + +module Create = +struct + module type S = + sig + include Intf.Base + end +end + +module Map1 = +struct + module type S = Intf.Name +end + +module Map2 = +struct + module type S = Intf.Name +end + +module type Product = +sig + module type S = S + (** Product deriver interface. *) + + module Make (P: S): Intf.S + (** Make deriver from product deriver. *) + + (** Reductions for reducing derivers. *) + module Reduce : + sig + module type S = Reduce.S + (** Reduction interface. *) + + (** Conjunctive reduction. *) + module Conjunctive : + sig + module type S = Reduce.Conjunctive.S + (** Conjunctive reduction interface. *) + + module Make (C: S): Reduce.S + (** Make reduction from conjunctive reduction. *) + end + end + + (** Unary reducing deriver. *) + module Reduce1 : + sig + module type S = Reduce1.S + (** Unary reducing deriver interface. *) + + module Make (R1: S): Intf.S + (** Make deriver from unary reducing deriver. *) + end + + (** Binary reducing deriver. *) + module Reduce2 : + sig + module type S = Reduce2.S + (** Binary reducing deriver interface. *) + + module Make (R2: S): Intf.S + (** Make deriver from binary reducing deriver. *) + end + + (** Unary creating deriver. *) + module Create : + sig + module type S = Create.S + (** Unary creating deriver interface. *) + + module Make (C: S): Intf.S + (** Make deriver from unary creating deriver. *) + end + + (** Unary mapping deriver. *) + module Map1 : + sig + module type S = Map1.S + (** Unary mapping deriver interface. *) + + module Make (M1: S): Intf.S + (** Make deriver from unary mapping deriver. *) + end + + (** Binary mapping deriver. *) + module Map2 : + sig + module type S = Map2.S + (** Binary mapping deriver interface. *) + + module Make (M2: S): Intf.S + (** Make deriver from binary mapping deriver. *) + end +end diff --git a/src/vendor/ppx_easy_deriving/util.ml b/src/vendor/ppx_easy_deriving/util.ml new file mode 100644 index 0000000000..fbb9d3f642 --- /dev/null +++ b/src/vendor/ppx_easy_deriving/util.ml @@ -0,0 +1,12 @@ +let reduce ~unit ~both = function + | [] -> unit + | [x] -> x + | xs -> + let xs = List.rev xs in + match xs with + | x :: xs -> + List.fold_right both (List.rev xs) x (* omits hash_empty *) + | [] -> assert false + +let map3 f l1 l2 l3 = + List.map2 (fun x1 (x2, x3) -> f x1 x2 x3) l1 (List.combine l2 l3) (* TODO: optimize *) diff --git a/src/vendor/ppx_easy_deriving/util.mli b/src/vendor/ppx_easy_deriving/util.mli new file mode 100644 index 0000000000..0f896e7efc --- /dev/null +++ b/src/vendor/ppx_easy_deriving/util.mli @@ -0,0 +1,8 @@ +(** Utility functions. *) + +val reduce: unit:'a -> both:('a -> 'a -> 'a) -> 'a list -> 'a +(** Reduce a list of values "optimally", + i.e. without unnecessary [~unit] and [~both]. *) + +val map3: ('a -> 'b -> 'c -> 'd) -> 'a list -> 'b list -> 'c list -> 'd list +(** Map three lists into one. *) diff --git a/src/witness/myARG.ml b/src/witness/myARG.ml index a4ab524a0e..6273ecdbd5 100644 --- a/src/witness/myARG.ml +++ b/src/witness/myARG.ml @@ -283,6 +283,7 @@ let partition_if_next if_next_n = module UnCilLogicIntra (Arg: SIntraOpt): SIntraOpt = struct open Cil + (* TODO: questionable (=) and (==) use here *) let is_equiv_stmtkind sk1 sk2 = match sk1, sk2 with | Instr is1, Instr is2 -> GobList.equal (=) is1 is2 diff --git a/src/witness/svcomp.ml b/src/witness/svcomp.ml index bb887e6cb1..81dbc53760 100644 --- a/src/witness/svcomp.ml +++ b/src/witness/svcomp.ml @@ -1,7 +1,6 @@ (** SV-COMP tasks and results. *) open GoblintCil -open Batteries module Specification = SvcompSpec @@ -27,9 +26,9 @@ let is_error_function f = (* TODO: unused, but should be used? *) let is_special_function f = let loc = f.vdecl in - let is_svcomp = String.ends_with loc.file "sv-comp.c" in (* only includes/sv-comp.c functions, not __VERIFIER_assert in benchmark *) + let is_svcomp = String.ends_with loc.file ~suffix:"sv-comp.c" in (* only includes/sv-comp.c functions, not __VERIFIER_assert in benchmark *) let is_verifier = match f.vname with - | fname when String.starts_with fname "__VERIFIER" -> true + | fname when String.starts_with fname ~prefix:"__VERIFIER" -> true | fname -> is_error_function f in is_svcomp && is_verifier @@ -60,6 +59,11 @@ struct | Unknown -> "unknown" end +exception Error of string + +let errorwith s = raise (Error s) + + module type TaskResult = sig module Arg: MyARG.S diff --git a/src/witness/witness.ml b/src/witness/witness.ml index fb88c2ce7b..5da46a1011 100644 --- a/src/witness/witness.ml +++ b/src/witness/witness.ml @@ -690,25 +690,17 @@ struct Timing.wrap "graphml witness" (write_file witness_path (module Task)) (module TaskResult) ) - let write entrystates = - match !AnalysisState.verified with - | Some false -> print_svcomp_result "ERROR (verify)" - | _ -> - if get_string "witness.yaml.validate" <> "" then ( - match get_bool "witness.yaml.strict" with - | true when !YamlWitness.cnt_error > 0 -> - print_svcomp_result "ERROR (witness error)" - | true when !YamlWitness.cnt_unsupported > 0 -> - print_svcomp_result "ERROR (witness unsupported)" - | true when !YamlWitness.cnt_disabled > 0 -> - print_svcomp_result "ERROR (witness disabled)" - | _ when !YamlWitness.cnt_refuted > 0 -> - print_svcomp_result (Result.to_string (False None)) - | _ when !YamlWitness.cnt_unconfirmed > 0 -> - print_svcomp_result (Result.to_string Unknown) - | _ -> - write entrystates - ) - else + let write yaml_validate_result entrystates = + match !AnalysisState.verified, !AnalysisState.unsound_both_branches_dead with + | _, Some true -> print_svcomp_result "ERROR (both branches dead)" + | Some false, _ -> print_svcomp_result "ERROR (verify)" + | _, _ -> + match yaml_validate_result with + | Some (Stdlib.Error msg) -> + print_svcomp_result ("ERROR (" ^ msg ^ ")") + | Some (Ok (Svcomp.Result.False _ | Unknown as result)) -> + print_svcomp_result (Result.to_string result) + | Some (Ok True) + | None -> write entrystates end diff --git a/src/witness/witnessUtil.ml b/src/witness/witnessUtil.ml index 5d22fd1e83..390e893987 100644 --- a/src/witness/witnessUtil.ml +++ b/src/witness/witnessUtil.ml @@ -63,6 +63,7 @@ struct List.exists (fun (_, edge) -> match edge with | Proc (_, Lval (Var fv, NoOffset), args) when LibraryFunctions.is_special fv -> + Goblint_backtrace.wrap_val ~mark:(Cilfacade.FunVarinfo fv) @@ fun () -> let desc = LibraryFunctions.find fv in begin match desc.special args with | Lock _ -> true @@ -209,7 +210,7 @@ struct let typ = TEnum (e, []) in let name = "enum " ^ ename in Hashtbl.replace genv name (Cabs2cil.EnvTyp typ, loc); - List.iter (fun (name, exp, loc) -> + List.iter (fun (name, _, exp, loc) -> Hashtbl.replace genv name (Cabs2cil.EnvEnum (exp, typ), loc) ) eitems | Cil.GVar (v, _, loc) diff --git a/src/witness/yamlWitness.ml b/src/witness/yamlWitness.ml index 42254f30de..2bdd2ced4c 100644 --- a/src/witness/yamlWitness.ml +++ b/src/witness/yamlWitness.ml @@ -43,13 +43,20 @@ struct specification } - let location ~location:(loc: Cil.location) ~(location_function): Location.t = { - file_name = loc.file; - file_hash = sha256_file loc.file; - line = loc.line; - column = loc.column; - function_ = location_function; - } + let location ~location:(loc: Cil.location) ~(location_function): Location.t = + let file_hash = + match GobConfig.get_string "witness.yaml.format-version" with + | "0.1" -> Some (sha256_file loc.file) + | "2.0" -> None + | _ -> assert false + in + { + file_name = loc.file; + file_hash; + line = loc.line; + column = Some loc.column; + function_ = Some location_function; + } let invariant invariant: Invariant.t = { string = invariant; @@ -514,6 +521,26 @@ struct Timing.wrap "yaml witness" write () end +let init () = + match GobConfig.get_string "witness.yaml.validate" with + | "" -> () + | path -> + (* Check witness existence before doing the analysis. *) + if not (Sys.file_exists path) then ( + Logs.error "witness.yaml.validate: %s not found" path; + Svcomp.errorwith "witness missing" + ) + +let loc_of_location (location: YamlWitnessType.Location.t): Cil.location = { + file = location.file_name; + line = location.line; + column = Option.value location.column ~default:1; + byte = -1; + endLine = -1; + endColumn = -1; + endByte = -1; + synthetic = false; +} module ValidationResult = struct @@ -553,17 +580,6 @@ struct module InvariantParser = WitnessUtil.InvariantParser module VR = ValidationResult - let loc_of_location (location: YamlWitnessType.Location.t): Cil.location = { - file = location.file_name; - line = location.line; - column = location.column; - byte = -1; - endLine = -1; - endColumn = -1; - endByte = -1; - synthetic = false; - } - let validate () = let location_locator = Locator.create () in let loop_locator = Locator.create () in @@ -581,7 +597,9 @@ struct let yaml = match Yaml_unix.of_file (Fpath.v (GobConfig.get_string "witness.yaml.validate")) with | Ok yaml -> yaml - | Error (`Msg m) -> failwith ("Yaml_unix.of_file: " ^ m) + | Error (`Msg m) -> + Logs.error "Yaml_unix.of_file: %s" m; + Svcomp.errorwith "witness missing" in let yaml_entries = yaml |> GobYaml.list |> BatResult.get_ok in @@ -795,6 +813,15 @@ struct None in + let validate_violation_sequence (violation_sequence: YamlWitnessType.ViolationSequence.t) = + (* TODO: update cnt-s appropriately (needs access to SV-COMP result pre-witness validation) *) + (* Nothing needs to be checked here! + If program is correct and we can prove it, we output true, which counts as refutation of violation witness. + If program is correct and we cannot prove it, we output unknown. + If program is incorrect, we output unknown. *) + None + in + match entry_type_enabled target_type, entry.entry_type with | true, LocationInvariant x -> validate_location_invariant x @@ -804,7 +831,9 @@ struct validate_precondition_loop_invariant x | true, InvariantSet x -> validate_invariant_set x - | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _ | InvariantSet _) -> + | true, ViolationSequence x -> + validate_violation_sequence x + | false, (LocationInvariant _ | LoopInvariant _ | PreconditionLoopInvariant _ | InvariantSet _ | ViolationSequence _) -> incr cnt_disabled; M.info_noloc ~category:Witness "disabled entry of type %s" target_type; None @@ -840,5 +869,19 @@ struct let certificate_path = GobConfig.get_string "witness.yaml.certificate" in if certificate_path <> "" then - yaml_entries_to_file (List.rev yaml_entries') (Fpath.v certificate_path) + yaml_entries_to_file (List.rev yaml_entries') (Fpath.v certificate_path); + + match GobConfig.get_bool "witness.yaml.strict" with + | true when !cnt_error > 0 -> + Error "witness error" + | true when !cnt_unsupported > 0 -> + Error "witness unsupported" + | true when !cnt_disabled > 0 -> + Error "witness disabled" + | _ when !cnt_refuted > 0 -> + Ok (Svcomp.Result.False None) + | _ when !cnt_unconfirmed > 0 -> + Ok Unknown + | _ -> + Ok True end diff --git a/src/witness/yamlWitnessType.ml b/src/witness/yamlWitnessType.ml index 1630e05b69..4fc2029801 100644 --- a/src/witness/yamlWitnessType.ml +++ b/src/witness/yamlWitnessType.ml @@ -106,29 +106,45 @@ module Location = struct type t = { file_name: string; - file_hash: string; + file_hash: string option; line: int; - column: int; - function_: string; + column: int option; + function_: string option; } [@@deriving ord] let to_yaml {file_name; file_hash; line; column; function_} = - `O [ - ("file_name", `String file_name); - ("file_hash", `String file_hash); - ("line", `Float (float_of_int line)); - ("column", `Float (float_of_int column)); - ("function", `String function_); - ] + `O ([ + ("file_name", `String file_name); + ] @ (match file_hash with + | Some file_hash -> [ + ("file_hash", `String file_hash); + ] + | None -> + [] + ) @ [ + ("line", `Float (float_of_int line)); + ] @ (match column with + | Some column -> [ + ("column", `Float (float_of_int column)); + ] + | None -> + [] + ) @ (match function_ with + | Some function_ -> [ + ("function", `String function_); + ] + | None -> + [] + )) let of_yaml y = let open GobYaml in let+ file_name = y |> find "file_name" >>= to_string - and+ file_hash = y |> find "file_hash" >>= to_string + and+ file_hash = y |> Yaml.Util.find "file_hash" >>= option_map to_string and+ line = y |> find "line" >>= to_int - and+ column = y |> find "column" >>= to_int - and+ function_ = y |> find "function" >>= to_string in + and+ column = y |> Yaml.Util.find "column" >>= option_map to_int + and+ function_ = y |> Yaml.Util.find "function" >>= option_map to_string in {file_name; file_hash; line; column; function_} end @@ -426,6 +442,205 @@ struct let entry_type = "precondition_loop_invariant_certificate" end +module ViolationSequence = +struct + + module Constraint = + struct + type t = { + value: string; + format: string option; + } + [@@deriving ord] + + let to_yaml {value; format} = + `O ([ + ("value", `String value); + ] @ (match format with + | Some format -> [ + ("format", `String format); + ] + | None -> + [] + )) + + let of_yaml y = + let open GobYaml in + let+ value = y |> find "value" >>= to_string + and+ format = y |> Yaml.Util.find "format" >>= option_map to_string in + {value; format} + end + + module Assumption = + struct + type t = { + location: Location.t; + action: string; + constraint_: Constraint.t; + } + [@@deriving ord] + + let waypoint_type = "assumption" + + let to_yaml' {location; action; constraint_} = + [ + ("location", Location.to_yaml location); + ("action", `String action); + ("constraint", Constraint.to_yaml constraint_); + ] + + let of_yaml y = + let open GobYaml in + let+ location = y |> find "location" >>= Location.of_yaml + and+ action = y |> find "action" >>= to_string + and+ constraint_ = y |> find "constraint" >>= Constraint.of_yaml in + {location; action; constraint_} + end + + module Target = + struct + type t = { + location: Location.t; + action: string; + } + [@@deriving ord] + + let waypoint_type = "target" + + let to_yaml' {location; action} = + [ + ("location", Location.to_yaml location); + ("action", `String action); + ] + + let of_yaml y = + let open GobYaml in + let+ location = y |> find "location" >>= Location.of_yaml + and+ action = y |> find "action" >>= to_string in + {location; action} + end + + module FunctionEnter = + struct + include Target + + let waypoint_type = "function_enter" + end + + module FunctionReturn = + struct + include Assumption + + let waypoint_type = "function_return" + end + + module Branching = + struct + include Assumption + + let waypoint_type = "branching" + end + + (* TODO: could maybe use GADT, but adds ugly existential layer to entry type pattern matching *) + module WaypointType = + struct + type t = + | Assumption of Assumption.t + | Target of Target.t + | FunctionEnter of FunctionEnter.t + | FunctionReturn of FunctionReturn.t + | Branching of Branching.t + [@@deriving ord] + + let waypoint_type = function + | Assumption _ -> Assumption.waypoint_type + | Target _ -> Target.waypoint_type + | FunctionEnter _ -> FunctionEnter.waypoint_type + | FunctionReturn _ -> FunctionReturn.waypoint_type + | Branching _ -> Branching.waypoint_type + + let to_yaml' = function + | Assumption x -> Assumption.to_yaml' x + | Target x -> Target.to_yaml' x + | FunctionEnter x -> FunctionEnter.to_yaml' x + | FunctionReturn x -> FunctionReturn.to_yaml' x + | Branching x -> Branching.to_yaml' x + + let of_yaml y = + let open GobYaml in + let* waypoint_type = y |> find "type" >>= to_string in + if waypoint_type = Assumption.waypoint_type then + let+ x = y |> Assumption.of_yaml in + Assumption x + else if waypoint_type = Target.waypoint_type then + let+ x = y |> Target.of_yaml in + Target x + else if waypoint_type = FunctionEnter.waypoint_type then + let+ x = y |> FunctionEnter.of_yaml in + FunctionEnter x + else if waypoint_type = FunctionReturn.waypoint_type then + let+ x = y |> FunctionReturn.of_yaml in + FunctionReturn x + else if waypoint_type = Branching.waypoint_type then + let+ x = y |> Branching.of_yaml in + Branching x + else + Error (`Msg "type") + end + + module Waypoint = + struct + type t = { + waypoint_type: WaypointType.t; + } + [@@deriving ord] + + let to_yaml {waypoint_type} = + `O [ + ("waypoint", `O ([ + ("type", `String (WaypointType.waypoint_type waypoint_type)); + ] @ WaypointType.to_yaml' waypoint_type) + ) + ] + + let of_yaml y = + let open GobYaml in + let+ waypoint_type = y |> find "waypoint" >>= WaypointType.of_yaml in + {waypoint_type} + end + + module Segment = + struct + type t = { + segment: Waypoint.t list; + } + [@@deriving ord] + + let to_yaml {segment} = + `O [("segment", `A (List.map Waypoint.to_yaml segment))] + + let of_yaml y = + let open GobYaml in + let+ segment = y |> find "segment" >>= list >>= list_map Waypoint.of_yaml in + {segment} + end + + type t = { + content: Segment.t list; + } + [@@deriving ord] + + let entry_type = "violation_sequence" + + let to_yaml' {content} = + [("content", `A (List.map Segment.to_yaml content))] + + let of_yaml y = + let open GobYaml in + let+ content = y |> find "content" >>= list >>= list_map Segment.of_yaml in + {content} +end + (* TODO: could maybe use GADT, but adds ugly existential layer to entry type pattern matching *) module EntryType = struct @@ -437,6 +652,7 @@ struct | LoopInvariantCertificate of LoopInvariantCertificate.t | PreconditionLoopInvariantCertificate of PreconditionLoopInvariantCertificate.t | InvariantSet of InvariantSet.t + | ViolationSequence of ViolationSequence.t [@@deriving ord] let entry_type = function @@ -447,6 +663,7 @@ struct | LoopInvariantCertificate _ -> LoopInvariantCertificate.entry_type | PreconditionLoopInvariantCertificate _ -> PreconditionLoopInvariantCertificate.entry_type | InvariantSet _ -> InvariantSet.entry_type + | ViolationSequence _ -> ViolationSequence.entry_type let to_yaml' = function | LocationInvariant x -> LocationInvariant.to_yaml' x @@ -456,6 +673,7 @@ struct | LoopInvariantCertificate x -> LoopInvariantCertificate.to_yaml' x | PreconditionLoopInvariantCertificate x -> PreconditionLoopInvariantCertificate.to_yaml' x | InvariantSet x -> InvariantSet.to_yaml' x + | ViolationSequence x -> ViolationSequence.to_yaml' x let of_yaml y = let open GobYaml in @@ -481,6 +699,9 @@ struct else if entry_type = InvariantSet.entry_type then let+ x = y |> InvariantSet.of_yaml in InvariantSet x + else if entry_type = ViolationSequence.entry_type then + let+ x = y |> ViolationSequence.of_yaml in + ViolationSequence x else Error (`Msg "entry_type") end diff --git a/tests/incremental/00-basic/01-global.c b/tests/incremental/00-basic/01-global.c index 8eac5b92a1..5b3a1feb6a 100644 --- a/tests/incremental/00-basic/01-global.c +++ b/tests/incremental/00-basic/01-global.c @@ -1,5 +1,6 @@ // Previosuly, the function was erroneously not reanalyzed when the global initializer/the start state changed // when hash-consing is activated. +// NOCHECK int g = 0; int main(){ diff --git a/tests/incremental/00-basic/01-global.patch b/tests/incremental/00-basic/01-global.patch index 2b86496f5a..9c74751174 100644 --- a/tests/incremental/00-basic/01-global.patch +++ b/tests/incremental/00-basic/01-global.patch @@ -3,6 +3,7 @@ @@ -1,6 +1,6 @@ // Previosuly, the function was erroneously not reanalyzed when the global initializer/the start state changed // when hash-consing is activated. + // NOCHECK -int g = 0; +int g = 35; diff --git a/tests/incremental/00-basic/04-rename_ids.c b/tests/incremental/00-basic/04-rename_ids.c index e5c9727edc..7f2884854d 100644 --- a/tests/incremental/00-basic/04-rename_ids.c +++ b/tests/incremental/00-basic/04-rename_ids.c @@ -1,3 +1,4 @@ +// NOCHECK int a; void b(); void c(); diff --git a/tests/incremental/00-basic/08-refine_interval_with_def_exc.c b/tests/incremental/00-basic/08-refine_interval_with_def_exc.c index da6cb9dd65..3fcf60e83a 100644 --- a/tests/incremental/00-basic/08-refine_interval_with_def_exc.c +++ b/tests/incremental/00-basic/08-refine_interval_with_def_exc.c @@ -1,3 +1,4 @@ +// NOCHECK struct input_state; typedef struct input_state input_state; struct input_state { diff --git a/tests/incremental/00-basic/12-rec-type.c b/tests/incremental/00-basic/12-rec-type.c index 7c862a107a..1c92e6f94b 100644 --- a/tests/incremental/00-basic/12-rec-type.c +++ b/tests/incremental/00-basic/12-rec-type.c @@ -4,7 +4,7 @@ typedef struct s_t s_t; union union_t { int i; - s_t *s[sizeof(s_t *)]; // This caused problems + s_t *s[sizeof(s_t *)]; // NOCRASH: This caused problems }; struct s_t { diff --git a/tests/incremental/00-basic/12-rec-type.patch b/tests/incremental/00-basic/12-rec-type.patch index 2a5bd8f478..5d00033464 100644 --- a/tests/incremental/00-basic/12-rec-type.patch +++ b/tests/incremental/00-basic/12-rec-type.patch @@ -5,8 +5,8 @@ index 7c862a107..a043e249c 100644 @@ -2,6 +2,7 @@ struct s_t ; typedef struct s_t s_t; - + +// Dummy change union union_t { int i; - s_t *s[sizeof(s_t *)]; // This caused problems + s_t *s[sizeof(s_t *)]; // NOCRASH: This caused problems diff --git a/tests/incremental/00-basic/15-reluctant-test.c b/tests/incremental/00-basic/15-reluctant-test.c index 6328061b29..a0c96977b7 100644 --- a/tests/incremental/00-basic/15-reluctant-test.c +++ b/tests/incremental/00-basic/15-reluctant-test.c @@ -1,5 +1,5 @@ #include -// This test used to resulted in an unreached fixpoint in the incremental implementation. +// FIXPOINT: This test used to resulted in an unreached fixpoint in the incremental implementation. int g = 3; diff --git a/tests/incremental/04-var-rename/01-rename_and_shuffle.c b/tests/incremental/04-var-rename/01-rename_and_shuffle.c index 7d6ea81e6f..0acafcae90 100644 --- a/tests/incremental/04-var-rename/01-rename_and_shuffle.c +++ b/tests/incremental/04-var-rename/01-rename_and_shuffle.c @@ -1,5 +1,5 @@ #include - +// CRAM // a is renamed to c, but the usage of a is replaced by b (semantic changes) int main() { int a = 0; diff --git a/tests/incremental/04-var-rename/01-rename_and_shuffle.patch b/tests/incremental/04-var-rename/01-rename_and_shuffle.patch index 94e27d9a80..fa6342b0ef 100644 --- a/tests/incremental/04-var-rename/01-rename_and_shuffle.patch +++ b/tests/incremental/04-var-rename/01-rename_and_shuffle.patch @@ -1,7 +1,7 @@ --- tests/incremental/04-var-rename/01-rename_and_shuffle.c +++ tests/incremental/04-var-rename/01-rename_and_shuffle.c @@ -2,10 +2,10 @@ - + // CRAM // a is renamed to c, but the usage of a is replaced by b (semantic changes) int main() { - int a = 0; diff --git a/tests/incremental/04-var-rename/02-rename_with_usage.c b/tests/incremental/04-var-rename/02-rename_with_usage.c index 2c93c487d8..928a8014d1 100644 --- a/tests/incremental/04-var-rename/02-rename_with_usage.c +++ b/tests/incremental/04-var-rename/02-rename_with_usage.c @@ -1,5 +1,5 @@ #include - +// CRAM //a is renamed to c, but its usages stay the same int main() { int a = 0; diff --git a/tests/incremental/04-var-rename/02-rename_with_usage.patch b/tests/incremental/04-var-rename/02-rename_with_usage.patch index 6cfe41bbb1..b6d0e4cb19 100644 --- a/tests/incremental/04-var-rename/02-rename_with_usage.patch +++ b/tests/incremental/04-var-rename/02-rename_with_usage.patch @@ -1,7 +1,7 @@ --- tests/incremental/04-var-rename/02-rename_with_usage.c +++ tests/incremental/04-var-rename/02-rename_with_usage.c @@ -2,10 +2,10 @@ - + // CRAM //a is renamed to c, but its usages stay the same int main() { - int a = 0; diff --git a/tests/incremental/04-var-rename/04-renamed_param.c b/tests/incremental/04-var-rename/04-renamed_param.c index 770af2683c..1d45695b1b 100644 --- a/tests/incremental/04-var-rename/04-renamed_param.c +++ b/tests/incremental/04-var-rename/04-renamed_param.c @@ -1,4 +1,5 @@ // function param is renamed (no semantic changes) +// CRAM void method(int a) { int c = a; } diff --git a/tests/incremental/04-var-rename/04-renamed_param.patch b/tests/incremental/04-var-rename/04-renamed_param.patch index 50a9b69f6a..7753ded6df 100644 --- a/tests/incremental/04-var-rename/04-renamed_param.patch +++ b/tests/incremental/04-var-rename/04-renamed_param.patch @@ -1,6 +1,7 @@ --- tests/incremental/04-var-rename/04-renamed_param.c +++ tests/incremental/04-var-rename/04-renamed_param.c @@ -2,5 +2,5 @@ + // CRAM -void method(int a) { - int c = a; +void method(int b) { diff --git a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.c b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.c index aed642566c..1453e5707a 100644 --- a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.c +++ b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.c @@ -1,5 +1,5 @@ //This test should mark foo and main as changed - +// CRAM void foo(int a, int b) { int x = a; int y = b; diff --git a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch index 9ffc2c1cea..7e46542911 100644 --- a/tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch +++ b/tests/incremental/04-var-rename/05-renamed_param_usage_changed.patch @@ -2,7 +2,7 @@ +++ tests/incremental/04-var-rename/05-renamed_param_usage_changed.c @@ -1,6 +1,6 @@ //This test should mark foo and main as changed - + // CRAM -void foo(int a, int b) { +void foo(int b, int a) { int x = a; diff --git a/tests/incremental/05-method-rename/00-simple_rename.c b/tests/incremental/05-method-rename/00-simple_rename.c index 5d1e6fe872..0dd7b4ba1c 100644 --- a/tests/incremental/05-method-rename/00-simple_rename.c +++ b/tests/incremental/05-method-rename/00-simple_rename.c @@ -1,5 +1,5 @@ #include - +// CRAM void foo() { printf("foo"); } diff --git a/tests/incremental/05-method-rename/00-simple_rename.patch b/tests/incremental/05-method-rename/00-simple_rename.patch index ed7b40014c..31f0bf18cc 100644 --- a/tests/incremental/05-method-rename/00-simple_rename.patch +++ b/tests/incremental/05-method-rename/00-simple_rename.patch @@ -2,7 +2,7 @@ +++ tests/incremental/05-method-rename/00-simple_rename.c @@ -1,10 +1,10 @@ #include - + // CRAM -void foo() { +void bar() { printf("foo"); diff --git a/tests/incremental/05-method-rename/01-dependent_rename.c b/tests/incremental/05-method-rename/01-dependent_rename.c index 66c1a5a634..c5c43d5cde 100644 --- a/tests/incremental/05-method-rename/01-dependent_rename.c +++ b/tests/incremental/05-method-rename/01-dependent_rename.c @@ -1,5 +1,5 @@ #include - +// CRAM void fun1() { printf("fun1"); } diff --git a/tests/incremental/05-method-rename/01-dependent_rename.patch b/tests/incremental/05-method-rename/01-dependent_rename.patch index f3a4a9a3f8..3712f75059 100644 --- a/tests/incremental/05-method-rename/01-dependent_rename.patch +++ b/tests/incremental/05-method-rename/01-dependent_rename.patch @@ -2,7 +2,7 @@ +++ tests/incremental/05-method-rename/01-dependent_rename.c @@ -1,14 +1,14 @@ #include - + // CRAM -void fun1() { +void bar1() { printf("fun1"); diff --git a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.c b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.c index 331a5e25cb..41813818cb 100644 --- a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.c +++ b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.c @@ -1,5 +1,5 @@ #include - +// CRAM void foo1(int c) { if (c < 10) foo2(c + 1); } diff --git a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch index 7f15d88c3a..3b62220898 100644 --- a/tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch +++ b/tests/incremental/05-method-rename/02-cyclic_rename_dependency.patch @@ -1,7 +1,7 @@ --- tests/incremental/05-method-rename/02-cyclic_rename_dependency.c +++ tests/incremental/05-method-rename/02-cyclic_rename_dependency.c @@ -2,14 +2,14 @@ - + // CRAM -void foo1(int c) { - if (c < 10) foo2(c + 1); +void bar1(int c) { diff --git a/tests/incremental/05-method-rename/03-cyclic_with_swap.c b/tests/incremental/05-method-rename/03-cyclic_with_swap.c index 34026afa92..898f15fe6d 100644 --- a/tests/incremental/05-method-rename/03-cyclic_with_swap.c +++ b/tests/incremental/05-method-rename/03-cyclic_with_swap.c @@ -1,5 +1,5 @@ #include - +// CRAM void foo1(int c) { if (c < 10) foo2(c + 1); } diff --git a/tests/incremental/05-method-rename/03-cyclic_with_swap.patch b/tests/incremental/05-method-rename/03-cyclic_with_swap.patch index 0886106162..3373449875 100644 --- a/tests/incremental/05-method-rename/03-cyclic_with_swap.patch +++ b/tests/incremental/05-method-rename/03-cyclic_with_swap.patch @@ -1,7 +1,7 @@ --- tests/incremental/05-method-rename/03-cyclic_with_swap.c +++ tests/incremental/05-method-rename/03-cyclic_with_swap.c @@ -2,13 +2,17 @@ - + // CRAM -void foo1(int c) { - if (c < 10) foo2(c + 1); +void newFun(int c) { diff --git a/tests/incremental/05-method-rename/04-deep_change.c b/tests/incremental/05-method-rename/04-deep_change.c index 80037f934d..4258329f2e 100644 --- a/tests/incremental/05-method-rename/04-deep_change.c +++ b/tests/incremental/05-method-rename/04-deep_change.c @@ -1,5 +1,5 @@ #include - +// CRAM void zap() { printf("zap"); } diff --git a/tests/incremental/05-method-rename/04-deep_change.patch b/tests/incremental/05-method-rename/04-deep_change.patch index 687b8f74bc..6d7ec21bf8 100644 --- a/tests/incremental/05-method-rename/04-deep_change.patch +++ b/tests/incremental/05-method-rename/04-deep_change.patch @@ -2,7 +2,7 @@ +++ tests/incremental/05-method-rename/04-deep_change.c @@ -1,7 +1,7 @@ #include - + // CRAM void zap() { - printf("zap"); + printf("drap"); diff --git a/tests/incremental/05-method-rename/05-common_rename.c b/tests/incremental/05-method-rename/05-common_rename.c index ce72a6dda1..643bba554a 100644 --- a/tests/incremental/05-method-rename/05-common_rename.c +++ b/tests/incremental/05-method-rename/05-common_rename.c @@ -1,5 +1,5 @@ #include - +// CRAM void foo() { printf("foo"); } diff --git a/tests/incremental/05-method-rename/05-common_rename.patch b/tests/incremental/05-method-rename/05-common_rename.patch index 93904d5780..dcf6d15719 100644 --- a/tests/incremental/05-method-rename/05-common_rename.patch +++ b/tests/incremental/05-method-rename/05-common_rename.patch @@ -2,7 +2,7 @@ +++ tests/incremental/05-method-rename/05-common_rename.c @@ -1,20 +1,20 @@ #include - + // CRAM -void foo() { +void bar() { printf("foo"); diff --git a/tests/incremental/05-method-rename/06-recursive_rename.c b/tests/incremental/05-method-rename/06-recursive_rename.c index dc9ac72e94..3d1dd2b9d1 100644 --- a/tests/incremental/05-method-rename/06-recursive_rename.c +++ b/tests/incremental/05-method-rename/06-recursive_rename.c @@ -1,3 +1,4 @@ +// CRAM void foo(int x) { if(x > 1) foo(x - 1); } diff --git a/tests/incremental/05-method-rename/06-recursive_rename.patch b/tests/incremental/05-method-rename/06-recursive_rename.patch index 356f959256..e29be521be 100644 --- a/tests/incremental/05-method-rename/06-recursive_rename.patch +++ b/tests/incremental/05-method-rename/06-recursive_rename.patch @@ -1,6 +1,7 @@ --- tests/incremental/05-method-rename/06-recursive_rename.c +++ tests/incremental/05-method-rename/06-recursive_rename.c -@@ -1,7 +1,7 @@ +@@ -1,8 +1,8 @@ + // CRAM -void foo(int x) { - if(x > 1) foo(x - 1); +void bar(int x) { diff --git a/tests/incremental/06-glob-var-rename/00-simple_rename.c b/tests/incremental/06-glob-var-rename/00-simple_rename.c index 56650e98ed..c06ba3a85a 100644 --- a/tests/incremental/06-glob-var-rename/00-simple_rename.c +++ b/tests/incremental/06-glob-var-rename/00-simple_rename.c @@ -1,5 +1,5 @@ #include - +// CRAM int foo = 1; int main() { diff --git a/tests/incremental/06-glob-var-rename/00-simple_rename.patch b/tests/incremental/06-glob-var-rename/00-simple_rename.patch index 1e0f3b2565..b9d20af358 100644 --- a/tests/incremental/06-glob-var-rename/00-simple_rename.patch +++ b/tests/incremental/06-glob-var-rename/00-simple_rename.patch @@ -2,7 +2,7 @@ +++ tests/incremental/06-glob-var-rename/00-simple_rename.c @@ -1,9 +1,9 @@ #include - + // CRAM -int foo = 1; +int bar = 1; diff --git a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c index 9ad715e50d..35e527225c 100644 --- a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c +++ b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.c @@ -1,5 +1,5 @@ #include - +// CRAM int foo = 1; int main() { diff --git a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch index 1d65c5672a..2c44da4cce 100644 --- a/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch +++ b/tests/incremental/06-glob-var-rename/01-duplicate_local_global.patch @@ -2,7 +2,7 @@ +++ tests/incremental/06-glob-var-rename/01-duplicate_local_global.c @@ -1,14 +1,14 @@ #include - + // CRAM -int foo = 1; +int bar = 1; diff --git a/tests/incremental/06-glob-var-rename/02-add_new_gvar.c b/tests/incremental/06-glob-var-rename/02-add_new_gvar.c index 5efe319981..c399e1f784 100644 --- a/tests/incremental/06-glob-var-rename/02-add_new_gvar.c +++ b/tests/incremental/06-glob-var-rename/02-add_new_gvar.c @@ -1,5 +1,5 @@ #include - +// CRAM int myVar = 1; int main() { diff --git a/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch b/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch index f0c145107a..0ca97da64f 100644 --- a/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch +++ b/tests/incremental/06-glob-var-rename/02-add_new_gvar.patch @@ -2,7 +2,7 @@ +++ tests/incremental/06-glob-var-rename/02-add_new_gvar.c @@ -1,8 +1,9 @@ #include - + // CRAM int myVar = 1; +int foo = 1; diff --git a/tests/incremental/11-restart/00-justglob.c b/tests/incremental/11-restart/00-justglob.c index 61b059f8ea..1e35604d3f 100644 --- a/tests/incremental/11-restart/00-justglob.c +++ b/tests/incremental/11-restart/00-justglob.c @@ -1,2 +1,3 @@ int max_domains = 0; int main() {} +// CRAM diff --git a/tests/regression/00-sanity/18-parse-empty-array.c b/tests/regression/00-sanity/18-parse-empty-array.c index a38cbc3712..2205d412da 100644 --- a/tests/regression/00-sanity/18-parse-empty-array.c +++ b/tests/regression/00-sanity/18-parse-empty-array.c @@ -1,4 +1,5 @@ // Test for issue in https://github.com/goblint/cil/issues/19 +// NOCRASH static int a[]; static int a[] = {}; static int b[0] = {}; diff --git a/tests/regression/00-sanity/20-if-0-realnode.c b/tests/regression/00-sanity/20-if-0-realnode.c index 01b9ca74dd..52140324a6 100644 --- a/tests/regression/00-sanity/20-if-0-realnode.c +++ b/tests/regression/00-sanity/20-if-0-realnode.c @@ -1,3 +1,5 @@ +// CRAM + void stuff() { } diff --git a/tests/regression/00-sanity/20-if-0-realnode.t b/tests/regression/00-sanity/20-if-0-realnode.t index 32f06ecdd4..46a0e47a69 100644 --- a/tests/regression/00-sanity/20-if-0-realnode.t +++ b/tests/regression/00-sanity/20-if-0-realnode.t @@ -8,29 +8,29 @@ │ (body) ▼ ┌─────────────────────────────────────────┐ - │ 20-if-0-realnode.c:8:5-14:5 │ - │ (20-if-0-realnode.c:8:9-8:10) │ - │ [20-if-0-realnode.c:7:5-8:5 │ Neg(0) + │ 20-if-0-realnode.c:10:5-16:5 │ + │ (20-if-0-realnode.c:10:9-10:10) │ + │ [20-if-0-realnode.c:9:5-10:5 │ Neg(0) │ (unknown)] │ ─────────┐ - │ YAML loc: 20-if-0-realnode.c:8:5-14:5 │ │ + │ YAML loc: 20-if-0-realnode.c:10:5-16:5 │ │ │ GraphML: true; server: true │ ◀────────┘ └─────────────────────────────────────────┘ │ │ Pos(0) ▼ ┌─────────────────────────────────────────┐ - │ 20-if-0-realnode.c:10:9-10:16 │ - │ (20-if-0-realnode.c:10:9-10:16) │ - │ YAML loc: 20-if-0-realnode.c:10:9-10:16 │ + │ 20-if-0-realnode.c:12:9-12:16 │ + │ (20-if-0-realnode.c:12:9-12:16) │ + │ YAML loc: 20-if-0-realnode.c:12:9-12:16 │ │ GraphML: true; server: true │ └─────────────────────────────────────────┘ │ │ stuff() ▼ ┌─────────────────────────────────────────┐ - │ 20-if-0-realnode.c:15:5-15:13 │ - │ (20-if-0-realnode.c:15:12-15:13) │ - │ YAML loc: 20-if-0-realnode.c:15:5-15:13 │ + │ 20-if-0-realnode.c:17:5-17:13 │ + │ (20-if-0-realnode.c:17:12-17:13) │ + │ YAML loc: 20-if-0-realnode.c:17:5-17:13 │ │ GraphML: true; server: true │ └─────────────────────────────────────────┘ │ diff --git a/tests/regression/00-sanity/21-empty-loops.c b/tests/regression/00-sanity/21-empty-loops.c index f3f0feb2e5..220e5bf6e9 100644 --- a/tests/regression/00-sanity/21-empty-loops.c +++ b/tests/regression/00-sanity/21-empty-loops.c @@ -1,3 +1,4 @@ +// CRAM int main() { // non-deterministically make all variants live diff --git a/tests/regression/00-sanity/21-empty-loops.t b/tests/regression/00-sanity/21-empty-loops.t index 932a21f049..c9cc096d15 100644 --- a/tests/regression/00-sanity/21-empty-loops.t +++ b/tests/regression/00-sanity/21-empty-loops.t @@ -8,20 +8,20 @@ │ (body) ▼ ┌───────────────────────────────────────┐ - │ 21-empty-loops.c:57:3-57:31 │ + │ 21-empty-loops.c:58:3-58:31 │ │ (unknown) │ - │ [21-empty-loops.c:56:1-57:3 │ skip + │ [21-empty-loops.c:57:1-58:3 │ skip │ (unknown)] │ ───────┐ - │ YAML loc: 21-empty-loops.c:57:3-57:31 │ │ + │ YAML loc: 21-empty-loops.c:58:3-58:31 │ │ │ GraphML: true; server: true │ ◀──────┘ └───────────────────────────────────────┘ │ │ Neg(1) ▼ ┌───────────────────────────────────────┐ - │ 21-empty-loops.c:58:1-58:1 │ + │ 21-empty-loops.c:59:1-59:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:58:1-58:1 │ + │ YAML loc: 21-empty-loops.c:59:1-59:1 │ │ GraphML: true; server: true │ └───────────────────────────────────────┘ │ @@ -39,19 +39,19 @@ │ (body) ▼ ┌────────────────────────────────────────────┐ - │ 21-empty-loops.c:62:3-62:14 (synthetic) │ - │ (21-empty-loops.c:62:10-62:11 (synthetic)) │ Pos(1) - │ YAML loop: 21-empty-loops.c:62:3-62:14 │ ─────────┐ + │ 21-empty-loops.c:63:3-63:14 (synthetic) │ + │ (21-empty-loops.c:63:10-63:11 (synthetic)) │ Pos(1) + │ YAML loop: 21-empty-loops.c:63:3-63:14 │ ─────────┐ │ GraphML: true; server: false │ │ - │ loop: 21-empty-loops.c:62:3-62:14 │ ◀────────┘ + │ loop: 21-empty-loops.c:63:3-63:14 │ ◀────────┘ └────────────────────────────────────────────┘ │ │ Neg(1) ▼ ┌────────────────────────────────────────────┐ - │ 21-empty-loops.c:63:1-63:1 │ + │ 21-empty-loops.c:64:1-64:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:63:1-63:1 │ + │ YAML loc: 21-empty-loops.c:64:1-64:1 │ │ GraphML: true; server: true │ └────────────────────────────────────────────┘ │ @@ -63,18 +63,18 @@ $ graph-easy --as=boxart f_empty_goto_loop_suffix.dot ┌───────────────────────────────────────┐ - │ 21-empty-loops.c:75:3-75:11 │ - │ (21-empty-loops.c:75:3-75:11) │ - │ YAML loc: 21-empty-loops.c:75:3-75:11 │ + │ 21-empty-loops.c:76:3-76:11 │ + │ (21-empty-loops.c:76:3-76:11) │ + │ YAML loc: 21-empty-loops.c:76:3-76:11 │ │ GraphML: true; server: true │ └───────────────────────────────────────┘ │ │ suffix() ▼ ┌───────────────────────────────────────┐ - │ 21-empty-loops.c:76:1-76:1 │ + │ 21-empty-loops.c:77:1-77:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:76:1-76:1 │ + │ YAML loc: 21-empty-loops.c:77:1-77:1 │ │ GraphML: true; server: true │ ◀┐ └───────────────────────────────────────┘ │ │ │ @@ -90,11 +90,11 @@ │ (body) │ ▼ │ ┌───────────────────────────────────────┐ │ - │ 21-empty-loops.c:73:3-73:38 │ │ + │ 21-empty-loops.c:74:3-74:38 │ │ │ (unknown) │ │ - skip │ [21-empty-loops.c:72:1-73:3 │ │ + skip │ [21-empty-loops.c:73:1-74:3 │ │ ┌─────── │ (unknown)] │ │ - │ │ YAML loc: 21-empty-loops.c:73:3-73:38 │ │ + │ │ YAML loc: 21-empty-loops.c:74:3-74:38 │ │ └──────▶ │ GraphML: true; server: true │ ─┘ └───────────────────────────────────────┘ @@ -106,28 +106,28 @@ │ (body) ▼ ┌────────────────────────────────────────────┐ - │ 21-empty-loops.c:80:3-80:14 (synthetic) │ - │ (21-empty-loops.c:80:10-80:11 (synthetic)) │ Pos(1) - │ YAML loop: 21-empty-loops.c:80:3-80:14 │ ─────────┐ + │ 21-empty-loops.c:81:3-81:14 (synthetic) │ + │ (21-empty-loops.c:81:10-81:11 (synthetic)) │ Pos(1) + │ YAML loop: 21-empty-loops.c:81:3-81:14 │ ─────────┐ │ GraphML: true; server: false │ │ - │ loop: 21-empty-loops.c:80:3-80:14 │ ◀────────┘ + │ loop: 21-empty-loops.c:81:3-81:14 │ ◀────────┘ └────────────────────────────────────────────┘ │ │ Neg(1) ▼ ┌────────────────────────────────────────────┐ - │ 21-empty-loops.c:82:3-82:11 │ - │ (21-empty-loops.c:82:3-82:11) │ - │ YAML loc: 21-empty-loops.c:82:3-82:11 │ + │ 21-empty-loops.c:83:3-83:11 │ + │ (21-empty-loops.c:83:3-83:11) │ + │ YAML loc: 21-empty-loops.c:83:3-83:11 │ │ GraphML: true; server: true │ └────────────────────────────────────────────┘ │ │ suffix() ▼ ┌────────────────────────────────────────────┐ - │ 21-empty-loops.c:83:1-83:1 │ + │ 21-empty-loops.c:84:1-84:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:83:1-83:1 │ + │ YAML loc: 21-empty-loops.c:84:1-84:1 │ │ GraphML: true; server: true │ └────────────────────────────────────────────┘ │ @@ -145,18 +145,18 @@ │ (body) ▼ ┌──────────────────────────────────────┐ - │ 21-empty-loops.c:93:3-93:9 │ body() - │ (21-empty-loops.c:93:3-93:9) │ ─────────┐ - │ YAML loc: 21-empty-loops.c:93:3-93:9 │ │ + │ 21-empty-loops.c:94:3-94:9 │ body() + │ (21-empty-loops.c:94:3-94:9) │ ─────────┐ + │ YAML loc: 21-empty-loops.c:94:3-94:9 │ │ │ GraphML: true; server: true │ ◀────────┘ └──────────────────────────────────────┘ │ │ Neg(1) ▼ ┌──────────────────────────────────────┐ - │ 21-empty-loops.c:95:1-95:1 │ + │ 21-empty-loops.c:96:1-96:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:95:1-95:1 │ + │ YAML loc: 21-empty-loops.c:96:1-96:1 │ │ GraphML: true; server: true │ └──────────────────────────────────────┘ │ @@ -168,36 +168,36 @@ $ graph-easy --as=boxart f_nonempty_while_loop.dot - ┌───────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ │ - │ ┌────────────────────────────────────────────┐ │ - │ │ f_nonempty_while_loop() │ │ - │ └────────────────────────────────────────────┘ │ - │ │ │ body() - │ │ (body) │ - │ ▼ │ - ┌─────────────────────────────────────────┐ ┌────────────────────────────────────────────┐ │ - │ 21-empty-loops.c:101:5-101:11 │ │ 21-empty-loops.c:99:3-102:3 (synthetic) │ │ - │ (21-empty-loops.c:101:5-101:11) │ │ (21-empty-loops.c:99:10-99:11 (synthetic)) │ │ - │ YAML loc: 21-empty-loops.c:101:5-101:11 │ │ YAML loop: 21-empty-loops.c:99:3-102:3 │ │ - │ GraphML: true; server: true │ Pos(1) │ GraphML: true; server: false │ │ - │ │ ◀──────── │ loop: 21-empty-loops.c:99:3-102:3 │ ◀┘ - └─────────────────────────────────────────┘ └────────────────────────────────────────────┘ + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ │ + │ ┌──────────────────────────────────────────────┐ │ + │ │ f_nonempty_while_loop() │ │ + │ └──────────────────────────────────────────────┘ │ + │ │ │ body() + │ │ (body) │ + │ ▼ │ + ┌─────────────────────────────────────────┐ ┌──────────────────────────────────────────────┐ │ + │ 21-empty-loops.c:102:5-102:11 │ │ 21-empty-loops.c:100:3-103:3 (synthetic) │ │ + │ (21-empty-loops.c:102:5-102:11) │ │ (21-empty-loops.c:100:10-100:11 (synthetic)) │ │ + │ YAML loc: 21-empty-loops.c:102:5-102:11 │ │ YAML loop: 21-empty-loops.c:100:3-103:3 │ │ + │ GraphML: true; server: true │ Pos(1) │ GraphML: true; server: false │ │ + │ │ ◀──────── │ loop: 21-empty-loops.c:100:3-103:3 │ ◀┘ + └─────────────────────────────────────────┘ └──────────────────────────────────────────────┘ │ │ Neg(1) ▼ - ┌────────────────────────────────────────────┐ - │ 21-empty-loops.c:103:1-103:1 │ - │ (unknown) │ - │ YAML loc: 21-empty-loops.c:103:1-103:1 │ - │ GraphML: true; server: true │ - └────────────────────────────────────────────┘ + ┌──────────────────────────────────────────────┐ + │ 21-empty-loops.c:104:1-104:1 │ + │ (unknown) │ + │ YAML loc: 21-empty-loops.c:104:1-104:1 │ + │ GraphML: true; server: true │ + └──────────────────────────────────────────────┘ │ │ return ▼ - ┌────────────────────────────────────────────┐ - │ return of f_nonempty_while_loop() │ - └────────────────────────────────────────────┘ + ┌──────────────────────────────────────────────┐ + │ return of f_nonempty_while_loop() │ + └──────────────────────────────────────────────┘ $ graph-easy --as=boxart f_empty_goto_loop_prefix.dot @@ -208,29 +208,29 @@ │ (body) ▼ ┌─────────────────────────────────────────┐ - │ 21-empty-loops.c:112:3-112:11 │ - │ (21-empty-loops.c:112:3-112:11) │ - │ YAML loc: 21-empty-loops.c:112:3-112:11 │ + │ 21-empty-loops.c:113:3-113:11 │ + │ (21-empty-loops.c:113:3-113:11) │ + │ YAML loc: 21-empty-loops.c:113:3-113:11 │ │ GraphML: true; server: true │ └─────────────────────────────────────────┘ │ │ prefix() ▼ ┌─────────────────────────────────────────┐ - │ 21-empty-loops.c:115:3-115:38 │ + │ 21-empty-loops.c:116:3-116:38 │ │ (unknown) │ - │ [21-empty-loops.c:114:1-115:3 │ skip + │ [21-empty-loops.c:115:1-116:3 │ skip │ (unknown)] │ ───────┐ - │ YAML loc: 21-empty-loops.c:115:3-115:38 │ │ + │ YAML loc: 21-empty-loops.c:116:3-116:38 │ │ │ GraphML: true; server: true │ ◀──────┘ └─────────────────────────────────────────┘ │ │ Neg(1) ▼ ┌─────────────────────────────────────────┐ - │ 21-empty-loops.c:116:1-116:1 │ + │ 21-empty-loops.c:117:1-117:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:116:1-116:1 │ + │ YAML loc: 21-empty-loops.c:117:1-117:1 │ │ GraphML: true; server: true │ └─────────────────────────────────────────┘ │ @@ -248,28 +248,28 @@ │ (body) ▼ ┌──────────────────────────────────────────────┐ - │ 21-empty-loops.c:120:3-120:11 │ - │ (21-empty-loops.c:120:3-120:11) │ - │ YAML loc: 21-empty-loops.c:120:3-120:11 │ + │ 21-empty-loops.c:121:3-121:11 │ + │ (21-empty-loops.c:121:3-121:11) │ + │ YAML loc: 21-empty-loops.c:121:3-121:11 │ │ GraphML: true; server: true │ └──────────────────────────────────────────────┘ │ │ prefix() ▼ ┌──────────────────────────────────────────────┐ - │ 21-empty-loops.c:122:3-122:14 (synthetic) │ - │ (21-empty-loops.c:122:10-122:11 (synthetic)) │ Pos(1) - │ YAML loop: 21-empty-loops.c:122:3-122:14 │ ─────────┐ + │ 21-empty-loops.c:123:3-123:14 (synthetic) │ + │ (21-empty-loops.c:123:10-123:11 (synthetic)) │ Pos(1) + │ YAML loop: 21-empty-loops.c:123:3-123:14 │ ─────────┐ │ GraphML: true; server: false │ │ - │ loop: 21-empty-loops.c:122:3-122:14 │ ◀────────┘ + │ loop: 21-empty-loops.c:123:3-123:14 │ ◀────────┘ └──────────────────────────────────────────────┘ │ │ Neg(1) ▼ ┌──────────────────────────────────────────────┐ - │ 21-empty-loops.c:123:1-123:1 │ + │ 21-empty-loops.c:124:1-124:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:123:1-123:1 │ + │ YAML loc: 21-empty-loops.c:124:1-124:1 │ │ GraphML: true; server: true │ └──────────────────────────────────────────────┘ │ @@ -289,7 +289,7 @@ ┌─────────────────────────────────────────┐ │ unknown │ │ (unknown) │ skip - │ [21-empty-loops.c:127:1-128:3 │ ───────┐ + │ [21-empty-loops.c:128:1-129:3 │ ───────┐ │ (unknown)] │ │ │ GraphML: true; server: true │ ◀──────┘ └─────────────────────────────────────────┘ @@ -297,9 +297,9 @@ │ Neg(1) ▼ ┌─────────────────────────────────────────┐ - │ 21-empty-loops.c:131:1-131:1 │ + │ 21-empty-loops.c:132:1-132:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:131:1-131:1 │ + │ YAML loc: 21-empty-loops.c:132:1-132:1 │ │ GraphML: true; server: true │ └─────────────────────────────────────────┘ │ @@ -317,19 +317,19 @@ │ (body) ▼ ┌──────────────────────────────────────────────┐ - │ 21-empty-loops.c:135:3-137:3 (synthetic) │ - │ (21-empty-loops.c:135:10-135:11 (synthetic)) │ Pos(1) - │ YAML loop: 21-empty-loops.c:135:3-137:3 │ ─────────┐ + │ 21-empty-loops.c:136:3-138:3 (synthetic) │ + │ (21-empty-loops.c:136:10-136:11 (synthetic)) │ Pos(1) + │ YAML loop: 21-empty-loops.c:136:3-138:3 │ ─────────┐ │ GraphML: true; server: false │ │ - │ loop: 21-empty-loops.c:135:3-137:3 │ ◀────────┘ + │ loop: 21-empty-loops.c:136:3-138:3 │ ◀────────┘ └──────────────────────────────────────────────┘ │ │ Neg(1) ▼ ┌──────────────────────────────────────────────┐ - │ 21-empty-loops.c:138:1-138:1 │ + │ 21-empty-loops.c:139:1-139:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:138:1-138:1 │ + │ YAML loc: 21-empty-loops.c:139:1-139:1 │ │ GraphML: true; server: true │ └──────────────────────────────────────────────┘ │ @@ -347,20 +347,20 @@ │ (body) ▼ ┌─────────────────────────────────────────┐ - │ 21-empty-loops.c:143:3-143:42 │ + │ 21-empty-loops.c:144:3-144:42 │ │ (unknown) │ - │ [21-empty-loops.c:142:1-143:3 │ skip + │ [21-empty-loops.c:143:1-144:3 │ skip │ (unknown)] │ ───────┐ - │ YAML loc: 21-empty-loops.c:143:3-143:42 │ │ + │ YAML loc: 21-empty-loops.c:144:3-144:42 │ │ │ GraphML: true; server: true │ ◀──────┘ └─────────────────────────────────────────┘ │ │ Neg(1) ▼ ┌─────────────────────────────────────────┐ - │ 21-empty-loops.c:146:1-146:1 │ + │ 21-empty-loops.c:147:1-147:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:146:1-146:1 │ + │ YAML loc: 21-empty-loops.c:147:1-147:1 │ │ GraphML: true; server: true │ └─────────────────────────────────────────┘ │ @@ -380,7 +380,7 @@ ┌────────────────────────────────────────────────────────┐ │ unknown │ │ (unknown) │ skip - │ [21-empty-loops.c:150:1-151:3 │ ───────┐ + │ [21-empty-loops.c:151:1-152:3 │ ───────┐ │ (unknown)] │ │ │ GraphML: true; server: true │ ◀──────┘ └────────────────────────────────────────────────────────┘ @@ -388,9 +388,9 @@ │ Neg(1) ▼ ┌────────────────────────────────────────────────────────┐ - │ 21-empty-loops.c:155:1-155:1 │ + │ 21-empty-loops.c:156:1-156:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:155:1-155:1 │ + │ YAML loc: 21-empty-loops.c:156:1-156:1 │ │ GraphML: true; server: true │ └────────────────────────────────────────────────────────┘ │ @@ -408,20 +408,20 @@ │ (body) ▼ ┌─────────────────────────────────────────────────────────┐ - │ 21-empty-loops.c:160:3-160:59 │ + │ 21-empty-loops.c:161:3-161:59 │ │ (unknown) │ - │ [21-empty-loops.c:159:1-160:3 │ skip + │ [21-empty-loops.c:160:1-161:3 │ skip │ (unknown)] │ ───────┐ - │ YAML loc: 21-empty-loops.c:160:3-160:59 │ │ + │ YAML loc: 21-empty-loops.c:161:3-161:59 │ │ │ GraphML: true; server: true │ ◀──────┘ └─────────────────────────────────────────────────────────┘ │ │ Neg(1) ▼ ┌─────────────────────────────────────────────────────────┐ - │ 21-empty-loops.c:164:1-164:1 │ + │ 21-empty-loops.c:165:1-165:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:164:1-164:1 │ + │ YAML loc: 21-empty-loops.c:165:1-165:1 │ │ GraphML: true; server: true │ └─────────────────────────────────────────────────────────┘ │ @@ -441,7 +441,7 @@ ┌───────────────────────────────────────────────────────┐ │ unknown │ │ (unknown) │ skip - │ [21-empty-loops.c:168:1-169:3 │ ───────┐ + │ [21-empty-loops.c:169:1-170:3 │ ───────┐ │ (unknown)] │ │ │ GraphML: true; server: true │ ◀──────┘ └───────────────────────────────────────────────────────┘ @@ -449,9 +449,9 @@ │ Neg(1) ▼ ┌───────────────────────────────────────────────────────┐ - │ 21-empty-loops.c:174:1-174:1 │ + │ 21-empty-loops.c:175:1-175:1 │ │ (unknown) │ - │ YAML loc: 21-empty-loops.c:174:1-174:1 │ + │ YAML loc: 21-empty-loops.c:175:1-175:1 │ │ GraphML: true; server: true │ └───────────────────────────────────────────────────────┘ │ diff --git a/tests/regression/00-sanity/29-earlyglobs-insens.c b/tests/regression/00-sanity/29-earlyglobs-insens.c index 9d2f186c3e..e671522c51 100644 --- a/tests/regression/00-sanity/29-earlyglobs-insens.c +++ b/tests/regression/00-sanity/29-earlyglobs-insens.c @@ -1,2 +1,3 @@ // PARAM: --set ana.ctx_insens[+] base --enable exp.earlyglobs +// NOCRASH int main() {} diff --git a/tests/regression/00-sanity/33-hoare-over-paths.c b/tests/regression/00-sanity/33-hoare-over-paths.c index b30586fa59..e3af40cbb3 100644 --- a/tests/regression/00-sanity/33-hoare-over-paths.c +++ b/tests/regression/00-sanity/33-hoare-over-paths.c @@ -1,7 +1,7 @@ // PARAM: --set ana.path_sens[+] mutex #include #include - +// CRAM pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int main() { @@ -27,8 +27,8 @@ int main() { // NEW explanation: // using SensitiveDomain correctly keeps both paths as path-sensitivity demands - // TODO: manually check final join node in HTML - // cannot be automated because concrete execution cannot check + // cram test checks internal result + // cannot be automated with annotations because concrete execution cannot check // if _must_ lockset _may_ contain m on some path return 0; } diff --git a/tests/regression/00-sanity/33-hoare-over-paths.t b/tests/regression/00-sanity/33-hoare-over-paths.t new file mode 100644 index 0000000000..9f88f836b0 --- /dev/null +++ b/tests/regression/00-sanity/33-hoare-over-paths.t @@ -0,0 +1,228 @@ + $ goblint --set ana.path_sens[+] mutex --set result pretty-deterministic --set outfile pretty.txt 33-hoare-over-paths.c + [Success][Assert] Assertion "1" will succeed (33-hoare-over-paths.c:11:5-11:24) + [Success][Assert] Assertion "1" will succeed (33-hoare-over-paths.c:16:5-16:24) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + + $ cat pretty.txt + Mapping { + 33-hoare-over-paths.c:9:7-9:8(main) -> + PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + Global { + m -> mutex + } + Local { + r -> ⊤ + } + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} + 33-hoare-over-paths.c:10:5-10:10(main) -> + PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + Global { + m -> mutex + } + Local { + r -> + (Not {0}([-31,31])) + } + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} + 33-hoare-over-paths.c:11:5-11:24(main) -> + PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + Global { + m -> mutex + } + Local { + r -> 0 + } + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} + 33-hoare-over-paths.c:15:5-15:27(main) -> + PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + Global { + m -> mutex + } + Local { + r -> 0 + } + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} + 33-hoare-over-paths.c:16:5-16:24(main) -> + PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + Global { + m -> mutex + } + Local { + r -> 0 + } + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{m}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} + 33-hoare-over-paths.c:33:10-33:11(main) -> + PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + Global { + m -> mutex + } + Local { + r -> 0 + } + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{m}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{}), + (MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + Global { + m -> mutex + } + Local { + r -> 0 + } + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} + 33-hoare-over-paths.c:7:1-34:1(main) -> + PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + Global { + m -> mutex + } + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} + 33-hoare-over-paths.c:7:1-34:1(main) -> + PathSensitive (ProjectiveSet (MCP.D * map)):{(MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + Global { + m -> mutex + } + Temp { + RETURN -> 0 + } + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{m}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{}), + (MCP.D:[expRelation:(), + mallocWrapper:(wrapper call:Unknown node, unique calls:{}), + base:({ + Global { + m -> mutex + } + Temp { + RETURN -> 0 + } + }, {}, {}, {}), + threadid:(wrapper call:unknown node, Thread:[main], created:(current function:bot, callees:bot)), + threadflag:Singlethreaded, + threadreturn:true, + escape:{}, + mutexEvents:(), + access:(), + mutex:(lockset:{}, multiplicity:{}), + race:(), + mhp:(), + assert:(), + pthreadMutexType:()], map:{})} + OTHERS -> Not available + } diff --git a/tests/regression/00-sanity/37-long-double.c b/tests/regression/00-sanity/37-long-double.c index 01c9b8bb9b..b4eb5e29c8 100644 --- a/tests/regression/00-sanity/37-long-double.c +++ b/tests/regression/00-sanity/37-long-double.c @@ -1,3 +1,4 @@ +// CRAM int main() { long double l = 0.0L; return (int)l; diff --git a/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c index 94c0f3efeb..aa1bc24aa4 100644 --- a/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c +++ b/tests/regression/00-sanity/52-thread-unsafe-libfuns-single-thread.c @@ -1,4 +1,5 @@ // PARAM: --enable allglobs --set ana.activated[+] threadJoins +// CRAM #include #include diff --git a/tests/regression/01-cpa/38-enum.c b/tests/regression/01-cpa/38-enum.c index 818705843e..ff345b6ba7 100644 --- a/tests/regression/01-cpa/38-enum.c +++ b/tests/regression/01-cpa/38-enum.c @@ -1,7 +1,7 @@ // PARAM: --disable ana.int.interval --disable ana.int.def_exc --enable ana.int.enums void main(){ int n = 1; - for (; n; n++) { // fixed point not reached here + for (; n; n++) { // FIXPOINT: previously fixed point not reached here } return; } diff --git a/tests/regression/01-cpa/51-marshal.c b/tests/regression/01-cpa/51-marshal.c index bf4e8faebf..c718159e33 100644 --- a/tests/regression/01-cpa/51-marshal.c +++ b/tests/regression/01-cpa/51-marshal.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval +// NOCHECK void callee(int j) { j++; } diff --git a/tests/regression/01-cpa/55-def_exc-widen.c b/tests/regression/01-cpa/55-def_exc-widen.c index 23b724ae7b..c19f3ab62e 100644 --- a/tests/regression/01-cpa/55-def_exc-widen.c +++ b/tests/regression/01-cpa/55-def_exc-widen.c @@ -1,4 +1,5 @@ //PARAM: --disable ana.int.def_exc_widen_by_join +// NOTIMEOUT int main(int argc , char **argv ) { char buf[512]; diff --git a/tests/regression/01-cpa/56-def_exc-fp1.c b/tests/regression/01-cpa/56-def_exc-fp1.c index 69390b1e35..8e38a04531 100644 --- a/tests/regression/01-cpa/56-def_exc-fp1.c +++ b/tests/regression/01-cpa/56-def_exc-fp1.c @@ -1,6 +1,6 @@ // PARAM: --enable ana.sv-comp.functions // manually minimized from sv-benchmarks/c/seq-mthreaded/pals_lcr.3.ufo.UNBOUNDED.pals.c -// used to not reach fixpoint due to def_exc range +// FIXPOINT: used to not reach fixpoint due to def_exc range _Bool __VERIFIER_nondet_bool(void) ; _Bool mode1 ; diff --git a/tests/regression/01-cpa/57-def_exc-interval-inconsistent.c b/tests/regression/01-cpa/57-def_exc-interval-inconsistent.c index a550ea172f..b0ac9ae5ba 100644 --- a/tests/regression/01-cpa/57-def_exc-interval-inconsistent.c +++ b/tests/regression/01-cpa/57-def_exc-interval-inconsistent.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.def_exc --enable ana.int.interval --enable ana.sv-comp.functions --set sem.int.signed_overflow assume_none --set ana.int.refinement never -// used to crash in branch when is_bool returned true, but to_bool returned None on (0,[1,1]) +// NOCRASH: used to crash in branch when is_bool returned true, but to_bool returned None on (0,[1,1]) // manually minimized from sv-benchmarks/c/recursive/MultCommutative-2.c extern int __VERIFIER_nondet_int(void); diff --git a/tests/regression/01-cpa/71-widen-sides.c b/tests/regression/01-cpa/71-widen-sides.c index 522f6fae44..a93124c6e5 100644 --- a/tests/regression/01-cpa/71-widen-sides.c +++ b/tests/regression/01-cpa/71-widen-sides.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.ctx_insens "['base', 'mallocWrapper']" --enable ana.int.interval --sets solvers.td3.side_widen sides-local +// PARAM: --set ana.ctx_insens "['base', 'mallocWrapper']" --enable ana.int.interval --set solvers.td3.side_widen sides-local #include int further(int n) { diff --git a/tests/regression/02-base/34-builtin_va_list.c b/tests/regression/02-base/34-builtin_va_list.c index 2ab9168c68..f16d6fc0fd 100644 --- a/tests/regression/02-base/34-builtin_va_list.c +++ b/tests/regression/02-base/34-builtin_va_list.c @@ -1,3 +1,4 @@ +// NOCRASH #include int sum(int n, ...) { diff --git a/tests/regression/02-base/57-meet-def-exc.c b/tests/regression/02-base/57-meet-def-exc.c index d66b1120d6..cf24431f8c 100644 --- a/tests/regression/02-base/57-meet-def-exc.c +++ b/tests/regression/02-base/57-meet-def-exc.c @@ -1,3 +1,4 @@ +// NOCHECK void main(void) { int x; int i = 41; diff --git a/tests/regression/02-base/64-enums-minmax.c b/tests/regression/02-base/64-enums-minmax.c index 9d366aa56f..6c0d827cf0 100644 --- a/tests/regression/02-base/64-enums-minmax.c +++ b/tests/regression/02-base/64-enums-minmax.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.enums +// NOTIMEOUT int main(void) { unsigned char c; int top; diff --git a/tests/regression/02-base/69-ipmi-struct-blob-fixpoint.c b/tests/regression/02-base/69-ipmi-struct-blob-fixpoint.c index 3aa06d1820..8e7e42f0e6 100644 --- a/tests/regression/02-base/69-ipmi-struct-blob-fixpoint.c +++ b/tests/regression/02-base/69-ipmi-struct-blob-fixpoint.c @@ -30,7 +30,7 @@ static int ipmi_open(struct inode *inode, struct file *file) struct ipmi_file_private *priv; priv = kmalloc(sizeof(*priv), GFP_KERNEL); mutex_lock(&ipmi_mutex); - priv->file = file; // should reach fixpoint from priv side effect from here + priv->file = file; // FIXPOINT: should reach fixpoint from priv side effect from here ipmi_create_user(0, &ipmi_hndlrs, priv, &(priv->user)); file->private_data = priv; diff --git a/tests/regression/02-base/72-ad-widen-duplicate.c b/tests/regression/02-base/72-ad-widen-duplicate.c index 436ae70c93..c893d4ae79 100644 --- a/tests/regression/02-base/72-ad-widen-duplicate.c +++ b/tests/regression/02-base/72-ad-widen-duplicate.c @@ -1,4 +1,5 @@ // https://github.com/goblint/analyzer/issues/554 +// FIXPOINT int a; int main() { c(&a); } diff --git a/tests/regression/02-base/73-no-eval-on-write-offset.c b/tests/regression/02-base/73-no-eval-on-write-offset.c index da0e635dfb..780c244e9a 100644 --- a/tests/regression/02-base/73-no-eval-on-write-offset.c +++ b/tests/regression/02-base/73-no-eval-on-write-offset.c @@ -1,4 +1,5 @@ // PARAM: --enable exp.earlyglobs +// NOCRASH char a; int c; diff --git a/tests/regression/02-base/87-casts-dep-on-param.c b/tests/regression/02-base/87-casts-dep-on-param.c index 93a16e4bc3..a265b9db04 100644 --- a/tests/regression/02-base/87-casts-dep-on-param.c +++ b/tests/regression/02-base/87-casts-dep-on-param.c @@ -9,7 +9,7 @@ int g () { int a[10]; f(1, &i); - // check that i is an integer and can be used for array indexing without Goblint crashing + // NOCRASH: check that i is an integer and can be used for array indexing without Goblint crashing int r = a[i]; a[i] = r; diff --git a/tests/regression/03-practical/03-pthread_ptrs.c b/tests/regression/03-practical/03-pthread_ptrs.c index c619c994b2..0694698af8 100644 --- a/tests/regression/03-practical/03-pthread_ptrs.c +++ b/tests/regression/03-practical/03-pthread_ptrs.c @@ -1,5 +1,5 @@ #include - +// NOCHECK static pthread_t sid1 ; static pthread_t sid2 ; static pthread_t sid3 ; @@ -15,22 +15,22 @@ void *fn2(void) { extern void *fn3(void * a); -int main() { +int main() { /* normal call to fn1 */ pthread_create(&sid1, NULL, fn1, NULL); - - /* ignore parameter (cast parameter to void) call */ + + /* ignore parameter (cast parameter to void) call */ pthread_create(&sid2, NULL, (void *(*)(void * )) (& fn2), NULL); - + /* we create a unknown thread -- that can't be good */ pthread_create(&sid3, NULL, &fn3, NULL); pthread_join(sid3, NULL); - + pthread_join(sid2, NULL); - + pthread_join(sid1, NULL); - + return 0; } diff --git a/tests/regression/03-practical/21-pfscan_combine_minimal.c b/tests/regression/03-practical/21-pfscan_combine_minimal.c index 5054260216..abdef0627b 100644 --- a/tests/regression/03-practical/21-pfscan_combine_minimal.c +++ b/tests/regression/03-practical/21-pfscan_combine_minimal.c @@ -1,3 +1,4 @@ +// FIXPOINT #include struct __anonstruct_PQUEUE_63 { diff --git a/tests/regression/03-practical/22-nested-infinite-loops.c b/tests/regression/03-practical/22-nested-infinite-loops.c index f3e1fed23b..658bcbacb0 100644 --- a/tests/regression/03-practical/22-nested-infinite-loops.c +++ b/tests/regression/03-practical/22-nested-infinite-loops.c @@ -1,4 +1,5 @@ // https://github.com/goblint/analyzer/issues/231#issuecomment-868369123 +// NOCHECK: should check CFG? int main(void) { int x = 0; while(1) { diff --git a/tests/regression/03-practical/23-knot-timeout.c b/tests/regression/03-practical/23-knot-timeout.c index f972363cea..2fafe5d4da 100644 --- a/tests/regression/03-practical/23-knot-timeout.c +++ b/tests/regression/03-practical/23-knot-timeout.c @@ -1,5 +1,5 @@ // PARAM: --disable ana.int.def_exc_widen_by_join -// Used to timeout without ana.int.def_exc_widen_by_join +// NOTIMEOUT: Used to timeout without ana.int.def_exc_widen_by_join #include struct a { diff --git a/tests/regression/03-practical/35-base-mutex-macos.c b/tests/regression/03-practical/35-base-mutex-macos.c new file mode 100644 index 0000000000..a83022e91b --- /dev/null +++ b/tests/regression/03-practical/35-base-mutex-macos.c @@ -0,0 +1,20 @@ +// Intentionally no #include , because we want to imitate/debug MacOS construction on anything. +// CRAM +#define __PTHREAD_MUTEX_SIZE__ 56 + +struct _opaque_pthread_mutex_t { + long __sig; + char __opaque[__PTHREAD_MUTEX_SIZE__]; +}; + +typedef struct _opaque_pthread_mutex_t __darwin_pthread_mutex_t; +typedef __darwin_pthread_mutex_t pthread_mutex_t; + +#define _PTHREAD_MUTEX_SIG_init 0x32AAABA7 +#define PTHREAD_MUTEX_INITIALIZER {_PTHREAD_MUTEX_SIG_init, {0}} + +pthread_mutex_t A = PTHREAD_MUTEX_INITIALIZER; + +int main() { + return 0; +} diff --git a/tests/regression/03-practical/35-base-mutex-macos.t b/tests/regression/03-practical/35-base-mutex-macos.t new file mode 100644 index 0000000000..9e5f36d337 --- /dev/null +++ b/tests/regression/03-practical/35-base-mutex-macos.t @@ -0,0 +1,14 @@ + $ goblint --enable witness.yaml.enabled --disable witness.invariant.accessed --set pre.cppflags[+] -DGOBLINT_NO_PTHREAD_ONCE 35-base-mutex-macos.c + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 2 + dead: 0 + total lines: 2 + [Info][Witness] witness generation summary: + total generation entries: 1 + +There should be no invariants about __sig. +Base analysis should hide mutex contents. + + $ yamlWitnessStrip < witness.yml + - entry_type: invariant_set + content: [] diff --git a/tests/regression/03-practical/dune b/tests/regression/03-practical/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/03-practical/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) diff --git a/tests/regression/06-symbeq/40-var_eq-widen1.c b/tests/regression/06-symbeq/40-var_eq-widen1.c index 72e28c57a0..1cef7fe324 100644 --- a/tests/regression/06-symbeq/40-var_eq-widen1.c +++ b/tests/regression/06-symbeq/40-var_eq-widen1.c @@ -1,6 +1,6 @@ // PARAM: --set ana.activated[+] var_eq // manually minimized from sv-benchmarks/c/ldv-linux-3.16-rc1/205_9a_array_unsafes_linux-3.16-rc1.tar.xz-205_9a-drivers--net--usb--cx82310_eth.ko-entry_point.cil.out.i -// used to call widen incorrectly +// NOCRASH: used to call widen incorrectly typedef _Bool bool; void usb_bulk_msg(int *arg4, int x) { diff --git a/tests/regression/10-synch/07-thread_self_create.c b/tests/regression/10-synch/07-thread_self_create.c index 473a26a25b..18c5f3ee83 100644 --- a/tests/regression/10-synch/07-thread_self_create.c +++ b/tests/regression/10-synch/07-thread_self_create.c @@ -1,5 +1,5 @@ // PARAM: --set ana.activated[+] thread -// Checks termination of thread analysis with a thread who is its own single parent. +// NOTIMEOUT: Checks termination of thread analysis with a thread who is its own single parent. #include void *t_fun(void *arg) { diff --git a/tests/regression/10-synch/23-tid-partitioned-array.c b/tests/regression/10-synch/23-tid-partitioned-array.c index 0f4942ec0e..e0dc849fad 100644 --- a/tests/regression/10-synch/23-tid-partitioned-array.c +++ b/tests/regression/10-synch/23-tid-partitioned-array.c @@ -1,4 +1,5 @@ // PARAM: --set ana.activated[+] thread --set ana.base.arrays.domain partitioned +// NOCRASH #include void *t_fun(void *arg) { diff --git a/tests/regression/10-synch/24-tid-partitioned-array-global.c b/tests/regression/10-synch/24-tid-partitioned-array-global.c index b61daed77f..00685dc1c8 100644 --- a/tests/regression/10-synch/24-tid-partitioned-array-global.c +++ b/tests/regression/10-synch/24-tid-partitioned-array-global.c @@ -1,4 +1,5 @@ // PARAM: --set ana.activated[+] thread --set ana.base.arrays.domain partitioned +// NOCRASH #include pthread_t t_ids[10000]; diff --git a/tests/regression/10-synch/25-tid-array-malloc.c b/tests/regression/10-synch/25-tid-array-malloc.c index 4000577fd3..e59cd02e97 100644 --- a/tests/regression/10-synch/25-tid-array-malloc.c +++ b/tests/regression/10-synch/25-tid-array-malloc.c @@ -1,4 +1,5 @@ // PARAM: --set ana.activated[+] thread +// NOCRASH #include #include diff --git a/tests/regression/10-synch/26-tid-array-malloc-free.c b/tests/regression/10-synch/26-tid-array-malloc-free.c index e8f00d13ae..b070fd0d84 100644 --- a/tests/regression/10-synch/26-tid-array-malloc-free.c +++ b/tests/regression/10-synch/26-tid-array-malloc-free.c @@ -1,4 +1,5 @@ // PARAM: --set ana.activated[+] thread +// NOCRASH #include #include diff --git a/tests/regression/10-synch/27-tid-array-malloc-2.c b/tests/regression/10-synch/27-tid-array-malloc-2.c index 462901459a..d1b41843a7 100644 --- a/tests/regression/10-synch/27-tid-array-malloc-2.c +++ b/tests/regression/10-synch/27-tid-array-malloc-2.c @@ -1,4 +1,5 @@ -// PARAM: --set ana.activated[+] thread +// PARAM: --set ana.activated[+] thread +// NOCRASH #include #include @@ -19,7 +20,7 @@ int main() pthread_create(&t[tid], 0, thread, 0); tid++; pthread_create(&t[tid], 0, thread, 0); - + tid=0; pthread_join(t[tid], 0); tid++; diff --git a/tests/regression/13-privatized/01-priv_nr.t b/tests/regression/13-privatized/01-priv_nr.t new file mode 100644 index 0000000000..bbc285098a --- /dev/null +++ b/tests/regression/13-privatized/01-priv_nr.t @@ -0,0 +1,161 @@ +`protection` privatization: + + $ goblint --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --disable witness.invariant.other --set ana.base.privatization protection 01-priv_nr.c + [Success][Assert] Assertion "glob1 == 5" will succeed (01-priv_nr.c:22:3-22:30) + [Success][Assert] Assertion "t == 5" will succeed (01-priv_nr.c:12:3-12:26) + [Success][Assert] Assertion "glob1 == -10" will succeed (01-priv_nr.c:14:3-14:32) + [Success][Assert] Assertion "glob1 == 6" will succeed (01-priv_nr.c:26:3-26:30) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: location_invariant + location: + file_name: 01-priv_nr.c + file_hash: $FILE_HASH + line: 25 + column: 3 + function: main + location_invariant: + string: glob1 == 5 + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 01-priv_nr.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + location_invariant: + string: glob1 == 5 + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 01-priv_nr.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + location_invariant: + string: (unsigned long )arg == 0UL + type: assertion + format: C + +`mutex-meet` privatization: + + $ goblint --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --disable witness.invariant.other --set ana.base.privatization mutex-meet 01-priv_nr.c + [Success][Assert] Assertion "glob1 == 5" will succeed (01-priv_nr.c:22:3-22:30) + [Success][Assert] Assertion "t == 5" will succeed (01-priv_nr.c:12:3-12:26) + [Success][Assert] Assertion "glob1 == -10" will succeed (01-priv_nr.c:14:3-14:32) + [Success][Assert] Assertion "glob1 == 6" will succeed (01-priv_nr.c:26:3-26:30) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: location_invariant + location: + file_name: 01-priv_nr.c + file_hash: $FILE_HASH + line: 25 + column: 3 + function: main + location_invariant: + string: glob1 == 5 + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 01-priv_nr.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + location_invariant: + string: glob1 == 5 + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 01-priv_nr.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + location_invariant: + string: (unsigned long )arg == 0UL + type: assertion + format: C + +`write+lock` privatization: + + $ goblint --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --disable witness.invariant.other --set ana.base.privatization write+lock 01-priv_nr.c + [Success][Assert] Assertion "glob1 == 5" will succeed (01-priv_nr.c:22:3-22:30) + [Success][Assert] Assertion "t == 5" will succeed (01-priv_nr.c:12:3-12:26) + [Success][Assert] Assertion "glob1 == -10" will succeed (01-priv_nr.c:14:3-14:32) + [Success][Assert] Assertion "glob1 == 6" will succeed (01-priv_nr.c:26:3-26:30) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 19 + dead: 0 + total lines: 19 + [Info][Witness] witness generation summary: + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + + $ yamlWitnessStrip < witness.yml + - entry_type: location_invariant + location: + file_name: 01-priv_nr.c + file_hash: $FILE_HASH + line: 25 + column: 3 + function: main + location_invariant: + string: glob1 == 5 + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 01-priv_nr.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + location_invariant: + string: glob1 == 5 + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 01-priv_nr.c + file_hash: $FILE_HASH + line: 11 + column: 3 + function: t_fun + location_invariant: + string: (unsigned long )arg == 0UL + type: assertion + format: C diff --git a/tests/regression/13-privatized/59-smtprc_threadenter_path_minimal.c b/tests/regression/13-privatized/59-smtprc_threadenter_path_minimal.c index 5a9342cfd0..dcbc617e0b 100644 --- a/tests/regression/13-privatized/59-smtprc_threadenter_path_minimal.c +++ b/tests/regression/13-privatized/59-smtprc_threadenter_path_minimal.c @@ -48,7 +48,7 @@ int main(int argc , char **argv ) } pthread_mutex_unlock(& main_thread_count_mutex); - // lock gets here with two paths and crashes + // NOCRASH: lock gets here with two paths and crashes pthread_create(& c_tid, NULL, & thread_start, NULL); return (0); } \ No newline at end of file diff --git a/tests/regression/13-privatized/62-global-threadid.c b/tests/regression/13-privatized/62-global-threadid.c index 38d21700ea..b5f3c831bb 100644 --- a/tests/regression/13-privatized/62-global-threadid.c +++ b/tests/regression/13-privatized/62-global-threadid.c @@ -1,5 +1,5 @@ #include - +// NOCHECK pthread_t id; extern void magic(); diff --git a/tests/regression/13-privatized/63-access-threadspawn-lval.c b/tests/regression/13-privatized/63-access-threadspawn-lval.c index 7f98b129ab..9ebd29a89d 100644 --- a/tests/regression/13-privatized/63-access-threadspawn-lval.c +++ b/tests/regression/13-privatized/63-access-threadspawn-lval.c @@ -1,3 +1,4 @@ +// CRAM #include pthread_t id1; diff --git a/tests/regression/13-privatized/63-access-threadspawn-lval.t b/tests/regression/13-privatized/63-access-threadspawn-lval.t new file mode 100644 index 0000000000..313459637c --- /dev/null +++ b/tests/regression/13-privatized/63-access-threadspawn-lval.t @@ -0,0 +1,24 @@ +Should have (safe) write accesses to id1 and id2: + + $ goblint --enable allglobs 63-access-threadspawn-lval.c + [Error][Imprecise][Unsound] Function definition missing for magic2 (63-access-threadspawn-lval.c:21:3-21:12) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (63-access-threadspawn-lval.c:21:3-21:12) + [Info][Imprecise] Invalidating expressions: & A, & id2, & id1, & e (63-access-threadspawn-lval.c:21:3-21:12) + [Error][Imprecise][Unsound] Function definition missing for magic1 (63-access-threadspawn-lval.c:13:3-13:11) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (63-access-threadspawn-lval.c:13:3-13:11) + [Info][Imprecise] Invalidating expressions: & A, & id2, & id1 (63-access-threadspawn-lval.c:13:3-13:11) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 13 + dead: 0 + total lines: 13 + [Success][Race] Memory location id1 (safe): (63-access-threadspawn-lval.c:4:11-4:14) + write with [multi:false, thread:[main]] (conf. 110) (exp: & *((pthread_t * __restrict )(& id1))) (63-access-threadspawn-lval.c:27:3-27:37) + [Success][Race] Memory location id2 (safe): (63-access-threadspawn-lval.c:5:11-5:14) + write with [mhp:{created={[main, f@63-access-threadspawn-lval.c:27:3-27:37]}}, thread:[main]] (conf. 110) (exp: (pthread_t * __restrict )(& id2)) (63-access-threadspawn-lval.c:28:3-28:37) + write with [mhp:{created={[main, f@63-access-threadspawn-lval.c:27:3-27:37]}}, thread:[main]] (conf. 110) (exp: & *((pthread_t * __restrict )(& id2))) (63-access-threadspawn-lval.c:28:3-28:37) + [Info][Race] Memory locations race summary: + safe: 2 + vulnerable: 0 + unsafe: 0 + total memory locations: 2 + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/13-privatized/64-access-invalidate.c b/tests/regression/13-privatized/64-access-invalidate.c index 7a191a9650..1461d413e2 100644 --- a/tests/regression/13-privatized/64-access-invalidate.c +++ b/tests/regression/13-privatized/64-access-invalidate.c @@ -1,3 +1,4 @@ +// CRAM #include pthread_t id; diff --git a/tests/regression/13-privatized/64-access-invalidate.t b/tests/regression/13-privatized/64-access-invalidate.t new file mode 100644 index 0000000000..28227ec475 --- /dev/null +++ b/tests/regression/13-privatized/64-access-invalidate.t @@ -0,0 +1,21 @@ +Should have (safe) write access to id1 and magic2 invalidate to A: + + $ goblint --enable allglobs 64-access-invalidate.c + [Error][Imprecise][Unsound] Function definition missing for magic2 (64-access-invalidate.c:16:3-16:12) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (64-access-invalidate.c:16:3-16:12) + [Info][Imprecise] Invalidating expressions: & A, & id, & e (64-access-invalidate.c:16:3-16:12) + [Error][Imprecise][Unsound] Function definition missing for magic1 (64-access-invalidate.c:12:3-12:11) + [Info][Imprecise] INVALIDATING ALL GLOBALS! (64-access-invalidate.c:12:3-12:11) + [Info][Imprecise] Invalidating expressions: & A, & id (64-access-invalidate.c:12:3-12:11) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 10 + dead: 0 + total lines: 10 + [Success][Race] Memory location id (safe): (64-access-invalidate.c:4:11-4:13) + write with [multi:false, thread:[main]] (conf. 110) (exp: & *((pthread_t * __restrict )(& id))) (64-access-invalidate.c:21:3-21:36) + [Info][Race] Memory locations race summary: + safe: 1 + vulnerable: 0 + unsafe: 0 + total memory locations: 1 + [Error][Imprecise][Unsound] Function definition missing diff --git a/tests/regression/13-privatized/65-threadreturn-cpa-remove.c b/tests/regression/13-privatized/65-threadreturn-cpa-remove.c index 3bbe85b3ea..58d8c9ea2f 100644 --- a/tests/regression/13-privatized/65-threadreturn-cpa-remove.c +++ b/tests/regression/13-privatized/65-threadreturn-cpa-remove.c @@ -1,5 +1,5 @@ #include - +// NOCHECK int d; pthread_t g; enum { b } c() {} diff --git a/tests/regression/20-slr_term/01-no-int-context.c b/tests/regression/20-slr_term/01-no-int-context.c index d057098559..ce8d3cb833 100644 --- a/tests/regression/20-slr_term/01-no-int-context.c +++ b/tests/regression/20-slr_term/01-no-int-context.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.interval --set solver slr3t --disable ana.base.context.int - +// NOCHECK int f (int i) { // -2 return i+1; } // -3 void g(int j) { // -4 diff --git a/tests/regression/20-slr_term/02-global-inc.c b/tests/regression/20-slr_term/02-global-inc.c index cfb2432999..fc2f9100df 100644 --- a/tests/regression/20-slr_term/02-global-inc.c +++ b/tests/regression/20-slr_term/02-global-inc.c @@ -1,4 +1,4 @@ - +// NOCHECK int g = 0; int f = 0; diff --git a/tests/regression/20-slr_term/03-3ctxs.c b/tests/regression/20-slr_term/03-3ctxs.c index d0236960ea..b622549419 100644 --- a/tests/regression/20-slr_term/03-3ctxs.c +++ b/tests/regression/20-slr_term/03-3ctxs.c @@ -1,4 +1,4 @@ - +// NOCHECK int f(int j){ return j+1; } diff --git a/tests/regression/20-slr_term/05-selfloop.c b/tests/regression/20-slr_term/05-selfloop.c index 94286393a6..638d11cb1b 100644 --- a/tests/regression/20-slr_term/05-selfloop.c +++ b/tests/regression/20-slr_term/05-selfloop.c @@ -1,3 +1,4 @@ +// NOCHECK void f() { } void g() { } void h() { } diff --git a/tests/regression/20-slr_term/06-trylock_rc_slr.c b/tests/regression/20-slr_term/06-trylock_rc_slr.c index ab24b142f6..d6e3c3f31e 100644 --- a/tests/regression/20-slr_term/06-trylock_rc_slr.c +++ b/tests/regression/20-slr_term/06-trylock_rc_slr.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --set solver slr3t +// FIXPOINT #include pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/tests/regression/22-partitioned_arrays/08-unsupported.c b/tests/regression/22-partitioned_arrays/08-unsupported.c index 51e897d840..73f5871d9c 100644 --- a/tests/regression/22-partitioned_arrays/08-unsupported.c +++ b/tests/regression/22-partitioned_arrays/08-unsupported.c @@ -1,6 +1,7 @@ // PARAM: --enable ana.int.interval --disable exp.fast_global_inits --set ana.base.arrays.domain partitioned // This is just to test that the analysis does not cause problems for features that are not explicitly dealt with +// NOCHECK: what problems? int main(void) { callok(); } diff --git a/tests/regression/22-partitioned_arrays/12-was_problematic_2.c b/tests/regression/22-partitioned_arrays/12-was_problematic_2.c index fd57549008..2ff396feb4 100644 --- a/tests/regression/22-partitioned_arrays/12-was_problematic_2.c +++ b/tests/regression/22-partitioned_arrays/12-was_problematic_2.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned +// NOCHECK int main(void) { int arr[260]; diff --git a/tests/regression/22-partitioned_arrays/13-was_problematic_3.c b/tests/regression/22-partitioned_arrays/13-was_problematic_3.c index ff7e839753..1822af4f0a 100644 --- a/tests/regression/22-partitioned_arrays/13-was_problematic_3.c +++ b/tests/regression/22-partitioned_arrays/13-was_problematic_3.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned +// NOCHECK struct some_struct { int dir[7]; diff --git a/tests/regression/22-partitioned_arrays/16-refine-meet.c b/tests/regression/22-partitioned_arrays/16-refine-meet.c index 05a73a70ba..7f238ab858 100644 --- a/tests/regression/22-partitioned_arrays/16-refine-meet.c +++ b/tests/regression/22-partitioned_arrays/16-refine-meet.c @@ -1,4 +1,5 @@ // PARAM: --set ana.base.arrays.domain partitioned +// NOCHECK int garr[7]; int main(int argc, char **argv) diff --git a/tests/regression/22-partitioned_arrays/19-fixpoint.c b/tests/regression/22-partitioned_arrays/19-fixpoint.c index 1ac55216dd..30d7821db5 100644 --- a/tests/regression/22-partitioned_arrays/19-fixpoint.c +++ b/tests/regression/22-partitioned_arrays/19-fixpoint.c @@ -1,4 +1,5 @@ // PARAM: --set ana.base.arrays.domain partitioned +// FIXPOINT #include int stored_elements[20]; diff --git a/tests/regression/22-partitioned_arrays/20-more-fixpoint.c b/tests/regression/22-partitioned_arrays/20-more-fixpoint.c index 8181cf85dd..ab87e1944a 100644 --- a/tests/regression/22-partitioned_arrays/20-more-fixpoint.c +++ b/tests/regression/22-partitioned_arrays/20-more-fixpoint.c @@ -1,4 +1,5 @@ // PARAM: --set ana.base.arrays.domain partitioned +// FIXPOINT #include #include #include diff --git a/tests/regression/23-partitioned_arrays_last/08-unsupported.c b/tests/regression/23-partitioned_arrays_last/08-unsupported.c index 0b0a1baca9..cff075d786 100644 --- a/tests/regression/23-partitioned_arrays_last/08-unsupported.c +++ b/tests/regression/23-partitioned_arrays_last/08-unsupported.c @@ -1,6 +1,7 @@ // PARAM: --enable ana.int.interval --disable exp.fast_global_inits --set ana.base.arrays.domain partitioned --set ana.base.partition-arrays.keep-expr "last" // This is just to test that the analysis does not cause problems for features that are not explicitly dealt with +// NOCHECK: what problems? int main(void) { vla(); callok(); diff --git a/tests/regression/23-partitioned_arrays_last/12-was_problematic_2.c b/tests/regression/23-partitioned_arrays_last/12-was_problematic_2.c index 9523510472..d5593ffe8b 100644 --- a/tests/regression/23-partitioned_arrays_last/12-was_problematic_2.c +++ b/tests/regression/23-partitioned_arrays_last/12-was_problematic_2.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.base.partition-arrays.keep-expr "last" +// NOCHECK int main(void) { int arr[260]; diff --git a/tests/regression/24-octagon/03-previously_problematic_a.c b/tests/regression/24-octagon/03-previously_problematic_a.c index 986e2821fd..a266358408 100644 --- a/tests/regression/24-octagon/03-previously_problematic_a.c +++ b/tests/regression/24-octagon/03-previously_problematic_a.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/04-previously_problematic_b.c b/tests/regression/24-octagon/04-previously_problematic_b.c index 62649e925d..e9a239f66b 100644 --- a/tests/regression/24-octagon/04-previously_problematic_b.c +++ b/tests/regression/24-octagon/04-previously_problematic_b.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included typedef int wchar_t; typedef unsigned long size_t; diff --git a/tests/regression/24-octagon/05-previously_problematic_c.c b/tests/regression/24-octagon/05-previously_problematic_c.c index c260646c23..8d08c343ad 100644 --- a/tests/regression/24-octagon/05-previously_problematic_c.c +++ b/tests/regression/24-octagon/05-previously_problematic_c.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/06-previously_problematic_d.c b/tests/regression/24-octagon/06-previously_problematic_d.c index 733b07d015..cca02f523b 100644 --- a/tests/regression/24-octagon/06-previously_problematic_d.c +++ b/tests/regression/24-octagon/06-previously_problematic_d.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/07-previously_problematic_e.c b/tests/regression/24-octagon/07-previously_problematic_e.c index cb9b12cafe..0dee19eb94 100644 --- a/tests/regression/24-octagon/07-previously_problematic_e.c +++ b/tests/regression/24-octagon/07-previously_problematic_e.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/08-previously_problematic_f.c b/tests/regression/24-octagon/08-previously_problematic_f.c index 431b8a6c57..93c4d3195f 100644 --- a/tests/regression/24-octagon/08-previously_problematic_f.c +++ b/tests/regression/24-octagon/08-previously_problematic_f.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/09-previously_problematic_g.c b/tests/regression/24-octagon/09-previously_problematic_g.c index c48184d4dd..910a28703d 100644 --- a/tests/regression/24-octagon/09-previously_problematic_g.c +++ b/tests/regression/24-octagon/09-previously_problematic_g.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/10-previously_problematic_h.c b/tests/regression/24-octagon/10-previously_problematic_h.c index 969ae965df..ff29145789 100644 --- a/tests/regression/24-octagon/10-previously_problematic_h.c +++ b/tests/regression/24-octagon/10-previously_problematic_h.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included int main(int argc, char const *argv[]) { diff --git a/tests/regression/24-octagon/11-previously_problematic_i.c b/tests/regression/24-octagon/11-previously_problematic_i.c index f1956919d8..517b8e58d2 100644 --- a/tests/regression/24-octagon/11-previously_problematic_i.c +++ b/tests/regression/24-octagon/11-previously_problematic_i.c @@ -1,5 +1,5 @@ // SKIP PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] apron -// These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might +// FIXPOINT: These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included char buf2[67]; diff --git a/tests/regression/24-octagon/12-previously_problematic_j.c b/tests/regression/24-octagon/12-previously_problematic_j.c index 208a89d50f..a289e70de3 100644 --- a/tests/regression/24-octagon/12-previously_problematic_j.c +++ b/tests/regression/24-octagon/12-previously_problematic_j.c @@ -1,4 +1,5 @@ // SKIP PARAM: --set ana.activated[+] apron +// NOCHECK void main(void) { int i = 0; int j = i; diff --git a/tests/regression/25-vla/03-calls.c b/tests/regression/25-vla/03-calls.c index 133e89f704..fbe38af248 100644 --- a/tests/regression/25-vla/03-calls.c +++ b/tests/regression/25-vla/03-calls.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval --disable ana.int.def_exc --set ana.base.arrays.domain partitioned // Variable-sized arrays +// NOCHECK void foo(int n, int a[n]); void foo2(int n, int a[30][n]); void foo3(int n, int a[n][30]); diff --git a/tests/regression/27-inv_invariants/02-bot-during-condition.c b/tests/regression/27-inv_invariants/02-bot-during-condition.c index a94c65bb81..88c5ff2ada 100644 --- a/tests/regression/27-inv_invariants/02-bot-during-condition.c +++ b/tests/regression/27-inv_invariants/02-bot-during-condition.c @@ -1,3 +1,4 @@ +// NOCHECK int main () { int tmp; diff --git a/tests/regression/27-inv_invariants/07-more-bot.c b/tests/regression/27-inv_invariants/07-more-bot.c index e4b677b599..8e5819158e 100644 --- a/tests/regression/27-inv_invariants/07-more-bot.c +++ b/tests/regression/27-inv_invariants/07-more-bot.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval // Adapted from sv-comp array-programs/partial_mod_count_1.c +// NOCHECK int N = 1000; int main(){ int i; diff --git a/tests/regression/28-race_reach/95-deref-fun.c b/tests/regression/28-race_reach/95-deref-fun.c index 04cf1a8723..f1431d9221 100644 --- a/tests/regression/28-race_reach/95-deref-fun.c +++ b/tests/regression/28-race_reach/95-deref-fun.c @@ -1,6 +1,6 @@ // PARAM: --enable ana.sv-comp.enabled --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" #include - +// NOCRASH void foo(int (*callback)()) { } diff --git a/tests/regression/28-race_reach/96-deref-fun2.c b/tests/regression/28-race_reach/96-deref-fun2.c index 9e76c8604d..cb3aee7535 100644 --- a/tests/regression/28-race_reach/96-deref-fun2.c +++ b/tests/regression/28-race_reach/96-deref-fun2.c @@ -1,6 +1,6 @@ // PARAM: --enable ana.sv-comp.enabled --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" #include - +// NOCRASH #define SVCOMP 1 #include diff --git a/tests/regression/29-svcomp/05-isp1362-malloc-fun.c b/tests/regression/29-svcomp/05-isp1362-malloc-fun.c index b8ded7e341..f1c8dece14 100644 --- a/tests/regression/29-svcomp/05-isp1362-malloc-fun.c +++ b/tests/regression/29-svcomp/05-isp1362-malloc-fun.c @@ -1,5 +1,5 @@ // PARAM: --set ana.malloc.wrappers "['ldv_malloc']" - +// NOCRASH #include typedef unsigned long __kernel_ulong_t; diff --git a/tests/regression/29-svcomp/11-arithmetic-bot.c b/tests/regression/29-svcomp/11-arithmetic-bot.c index ce8635b939..d365f14a31 100644 --- a/tests/regression/29-svcomp/11-arithmetic-bot.c +++ b/tests/regression/29-svcomp/11-arithmetic-bot.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval --enable ana.int.def_exc // from: ldv-linux-3.0/usb_urb-drivers-vhost-vhost_net.ko.cil.out.i +// NOCRASH typedef unsigned long long u64; int main( ) diff --git a/tests/regression/29-svcomp/12-interval-bot.c b/tests/regression/29-svcomp/12-interval-bot.c index 77739efdbd..9d6157875a 100644 --- a/tests/regression/29-svcomp/12-interval-bot.c +++ b/tests/regression/29-svcomp/12-interval-bot.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.interval --enable ana.int.def_exc - +// NOCRASH int main(){ unsigned long long a ; diff --git a/tests/regression/29-svcomp/13-comparision-bot.c b/tests/regression/29-svcomp/13-comparision-bot.c index f64fa8a349..843a5fabb4 100644 --- a/tests/regression/29-svcomp/13-comparision-bot.c +++ b/tests/regression/29-svcomp/13-comparision-bot.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --enable ana.int.def_exc +// NOCRASH #include int main(){ int a = 0; diff --git a/tests/regression/29-svcomp/14-addition-in-comparision-bot.c b/tests/regression/29-svcomp/14-addition-in-comparision-bot.c index f667ca88f6..c729cf6019 100644 --- a/tests/regression/29-svcomp/14-addition-in-comparision-bot.c +++ b/tests/regression/29-svcomp/14-addition-in-comparision-bot.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval +// NOCRASH int main() { unsigned int top; diff --git a/tests/regression/29-svcomp/19-problematic.c b/tests/regression/29-svcomp/19-problematic.c index 63b4fc7af0..99c7c01197 100644 --- a/tests/regression/29-svcomp/19-problematic.c +++ b/tests/regression/29-svcomp/19-problematic.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.sv-comp.functions // Adapted from: https://github.com/sosy-lab/sv-benchmarks/blob/master/c/ldv-regression/test27-2.c +// NOTIMEOUT extern int __VERIFIER_nondet_int(void); struct dummy { diff --git a/tests/regression/29-svcomp/21-issue-casting.c b/tests/regression/29-svcomp/21-issue-casting.c index e02989559c..35d81d4898 100644 --- a/tests/regression/29-svcomp/21-issue-casting.c +++ b/tests/regression/29-svcomp/21-issue-casting.c @@ -1,6 +1,7 @@ // PARAM: --set ana.activated ["'base'","'mallocWrapper'"] --set ana.base.privatization none // minimal analyses to reveal bug // none privatization because mutex deactivated +// NOTIMEOUT static long main(void) { unsigned int cmd; diff --git a/tests/regression/29-svcomp/25-writing-into-char-array.c b/tests/regression/29-svcomp/25-writing-into-char-array.c index 8894007d23..ce8fea40f4 100644 --- a/tests/regression/29-svcomp/25-writing-into-char-array.c +++ b/tests/regression/29-svcomp/25-writing-into-char-array.c @@ -1,3 +1,4 @@ +// NOCHECK #include struct __anonstruct_mbox_t_232 { int one; diff --git a/tests/regression/29-svcomp/26-ikinds_if.c b/tests/regression/29-svcomp/26-ikinds_if.c index b902659f47..a8f726353f 100644 --- a/tests/regression/29-svcomp/26-ikinds_if.c +++ b/tests/regression/29-svcomp/26-ikinds_if.c @@ -1,4 +1,5 @@ //PARAM: --enable ana.int.interval +// NOCHECK #include static long sound_ioctl(unsigned int cmd , unsigned long arg ) diff --git a/tests/regression/29-svcomp/29-witness.c b/tests/regression/29-svcomp/29-witness.c index 72d1c4435a..b56b47a74c 100644 --- a/tests/regression/29-svcomp/29-witness.c +++ b/tests/regression/29-svcomp/29-witness.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.sv-comp.enabled --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" +// NOCRASH #include #include diff --git a/tests/regression/29-svcomp/32-no-ov.c b/tests/regression/29-svcomp/32-no-ov.c index 0167098c29..3612b34459 100644 --- a/tests/regression/29-svcomp/32-no-ov.c +++ b/tests/regression/29-svcomp/32-no-ov.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.interval --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" - +// CRAM int main(){ // This is not an overflow, just implementation defined behavior on a cast int data = ((int)(rand() & 1 ? (((unsigned)rand()<<30) ^ ((unsigned)rand()<<15) ^ rand()) : -(((unsigned)rand()<<30) ^ ((unsigned)rand()<<15) ^ rand()) - 1)); diff --git a/tests/regression/29-svcomp/36-svcomp-arch.c b/tests/regression/29-svcomp/36-svcomp-arch.c new file mode 100644 index 0000000000..ea68ba187c --- /dev/null +++ b/tests/regression/29-svcomp/36-svcomp-arch.c @@ -0,0 +1,8 @@ +// CRAM +#include + +int main() { + long k = INT_MAX; + long n = k * k; + return 0; +} diff --git a/tests/regression/29-svcomp/36-svcomp-arch.t b/tests/regression/29-svcomp/36-svcomp-arch.t new file mode 100644 index 0000000000..a0715e3872 --- /dev/null +++ b/tests/regression/29-svcomp/36-svcomp-arch.t @@ -0,0 +1,22 @@ +There should be overflow on ILP32: + + $ goblint --enable ana.sv-comp.enabled --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" --set exp.architecture 32bit 36-svcomp-arch.c + [Info] Setting "ana.int.interval" to true + [Info] SV-COMP specification: CHECK( init(main()), LTL(G ! overflow) ) + [Warning][Integer > Overflow][CWE-190] Signed integer overflow (36-svcomp-arch.c:6:8-6:17) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 4 + dead: 0 + total lines: 4 + SV-COMP result: unknown + +There shouldn't be an overflow on LP64: + + $ goblint --enable ana.sv-comp.enabled --set ana.specification "CHECK( init(main()), LTL(G ! overflow) )" --set exp.architecture 64bit 36-svcomp-arch.c + [Info] Setting "ana.int.interval" to true + [Info] SV-COMP specification: CHECK( init(main()), LTL(G ! overflow) ) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 4 + dead: 0 + total lines: 4 + SV-COMP result: true diff --git a/tests/regression/29-svcomp/dune b/tests/regression/29-svcomp/dune index 2aede4e50b..95ac66a5ec 100644 --- a/tests/regression/29-svcomp/dune +++ b/tests/regression/29-svcomp/dune @@ -14,3 +14,7 @@ (cram (deps (glob_files *.c))) + +(cram + (applies_to 36-svcomp-arch) + (enabled_if (<> %{system} macosx))) ; https://dune.readthedocs.io/en/stable/reference/boolean-language.html diff --git a/tests/regression/31-ikind-aware-ints/03-lnot.c b/tests/regression/31-ikind-aware-ints/03-lnot.c index 2fb6d79c61..6d96d88dea 100644 --- a/tests/regression/31-ikind-aware-ints/03-lnot.c +++ b/tests/regression/31-ikind-aware-ints/03-lnot.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval +// NOCRASH int main() { unsigned int l = 0; diff --git a/tests/regression/31-ikind-aware-ints/04-ptrdiff.c b/tests/regression/31-ikind-aware-ints/04-ptrdiff.c index 4be48c7793..be658b3cd0 100644 --- a/tests/regression/31-ikind-aware-ints/04-ptrdiff.c +++ b/tests/regression/31-ikind-aware-ints/04-ptrdiff.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval --set ana.base.arrays.domain partitioned --set ana.activated[+] var_eq +// NOCRASH int *tmp; int main () diff --git a/tests/regression/31-ikind-aware-ints/05-shift.c b/tests/regression/31-ikind-aware-ints/05-shift.c index a0cf2182d8..87bfba5a0b 100644 --- a/tests/regression/31-ikind-aware-ints/05-shift.c +++ b/tests/regression/31-ikind-aware-ints/05-shift.c @@ -1,6 +1,6 @@ // PARAM: --enable ana.int.interval int main(void) { - // Shifting by a negative number is UB, but we should still not crash on it, but go to top instead + // NOCRASH: Shifting by a negative number is UB, but we should still not crash on it, but go to top instead int v = -1; int r = 17; int u = r >> v; diff --git a/tests/regression/31-ikind-aware-ints/06-structs.c b/tests/regression/31-ikind-aware-ints/06-structs.c index ddf3b51cf0..d3c944c4cd 100644 --- a/tests/regression/31-ikind-aware-ints/06-structs.c +++ b/tests/regression/31-ikind-aware-ints/06-structs.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval +// NOCRASH struct rtl8169_private { unsigned int features ; }; diff --git a/tests/regression/31-ikind-aware-ints/15-strange.c b/tests/regression/31-ikind-aware-ints/15-strange.c index d063b3c6e2..550baf0e1d 100644 --- a/tests/regression/31-ikind-aware-ints/15-strange.c +++ b/tests/regression/31-ikind-aware-ints/15-strange.c @@ -1,7 +1,7 @@ //PARAM: --disable ana.int.interval --disable ana.int.def_exc --enable ana.int.enums int main (int argc, char* argv[]) { - // This used to cause an exception because of incompatible ikinds + // NOCRASH: This used to cause an exception because of incompatible ikinds // See https://github.com/goblint/cil/issues/29 signed char f2 = 7; signed char l_1857 = ((0xFFBD4A17L && f2) | f2); diff --git a/tests/regression/31-ikind-aware-ints/17-def-enum-refine.c b/tests/regression/31-ikind-aware-ints/17-def-enum-refine.c index e34d2d1c79..fd3a75154e 100644 --- a/tests/regression/31-ikind-aware-ints/17-def-enum-refine.c +++ b/tests/regression/31-ikind-aware-ints/17-def-enum-refine.c @@ -4,7 +4,7 @@ int main() { _Bool c; if(c) { x--;} else { x--;} - // The veryfier claimed that the fixed-point was not reached here due to a bug in Enums.leq + // FIXPOINT: The veryfier claimed that the fixed-point was not reached here due to a bug in Enums.leq // The leq wrongly returned false for the Enums {0} and not{}[0,1] return 0; } diff --git a/tests/regression/33-constants/01-const.c b/tests/regression/33-constants/01-const.c index 8db984a4b3..436a389545 100644 --- a/tests/regression/33-constants/01-const.c +++ b/tests/regression/33-constants/01-const.c @@ -1,5 +1,6 @@ //PARAM: --set ana.activated '["constants"]' // intentional explicit ana.activated to do tutorial in isolation +// NOCHECK int f(int a, int b){ int d = 3; int z = a + d; diff --git a/tests/regression/33-constants/02-simple.c b/tests/regression/33-constants/02-simple.c index aa370aa4da..75cd33ee8a 100644 --- a/tests/regression/33-constants/02-simple.c +++ b/tests/regression/33-constants/02-simple.c @@ -1,6 +1,6 @@ //PARAM: --set ana.activated '["constants"]' // intentional explicit ana.activated to do tutorial in isolation - +// NOCHECK int main(){ int x = 3; int y = 4; diff --git a/tests/regression/33-constants/03-empty-not-dead-branch.c b/tests/regression/33-constants/03-empty-not-dead-branch.c index 2b6d662589..2bd6a10190 100644 --- a/tests/regression/33-constants/03-empty-not-dead-branch.c +++ b/tests/regression/33-constants/03-empty-not-dead-branch.c @@ -1,6 +1,6 @@ //PARAM: --set ana.activated '["constants"]' // intentional explicit ana.activated to do tutorial in isolation - +// NOCHECK int g; int main() { diff --git a/tests/regression/33-constants/04-empty-not-dead.c b/tests/regression/33-constants/04-empty-not-dead.c index 8ad4199aa8..377a066370 100644 --- a/tests/regression/33-constants/04-empty-not-dead.c +++ b/tests/regression/33-constants/04-empty-not-dead.c @@ -1,6 +1,6 @@ //PARAM: --set ana.activated '["constants"]' // intentional explicit ana.activated to do tutorial in isolation - +// NOCHECK int g; int main() { diff --git a/tests/regression/33-constants/05-fun_ptranal.c b/tests/regression/33-constants/05-fun_ptranal.c index 5ebaf24e22..bb8f2146e5 100644 --- a/tests/regression/33-constants/05-fun_ptranal.c +++ b/tests/regression/33-constants/05-fun_ptranal.c @@ -1,5 +1,6 @@ //PARAM: --set ana.activated '["constants", "ptranal"]' // intentional explicit ana.activated to do tutorial in isolation +// NOCHECK int f(int a, int b){ int d = 3; int z = a + d; diff --git a/tests/regression/34-localwn_restart/05-nested.w.counter.c b/tests/regression/34-localwn_restart/05-nested.w.counter.c index 2d0cca1853..7cc7c6ce59 100644 --- a/tests/regression/34-localwn_restart/05-nested.w.counter.c +++ b/tests/regression/34-localwn_restart/05-nested.w.counter.c @@ -1,4 +1,5 @@ // Variant of nested.c with a counter. +// NOCHECK void main() { int z = 0; diff --git a/tests/regression/35-marshaling/01-disable_hashcons.c b/tests/regression/35-marshaling/01-disable_hashcons.c index f6a72bd685..5be9e47819 100644 --- a/tests/regression/35-marshaling/01-disable_hashcons.c +++ b/tests/regression/35-marshaling/01-disable_hashcons.c @@ -1,2 +1,3 @@ // PARAM: --disable ana.opt.hashcons +// NOCRASH int main(void) { return 0; } diff --git a/tests/regression/36-apron/12-traces-min-rpb1.t b/tests/regression/36-apron/12-traces-min-rpb1.t new file mode 100644 index 0000000000..5060f505d9 --- /dev/null +++ b/tests/regression/36-apron/12-traces-min-rpb1.t @@ -0,0 +1,56 @@ + $ goblint --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --disable witness.invariant.other --disable ana.base.invariant.enabled --set ana.relation.privatization mutex-meet --set ana.activated[+] apron --enable ana.sv-comp.functions --set ana.apron.domain polyhedra 12-traces-min-rpb1.c + [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:16:3-16:26) + [Warning][Assert] Assertion "g == h" is unknown. (12-traces-min-rpb1.c:27:3-27:26) + [Success][Assert] Assertion "g == h" will succeed (12-traces-min-rpb1.c:29:3-29:26) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 18 + dead: 0 + total lines: 18 + [Warning][Race] Memory location h (race with conf. 110): (12-traces-min-rpb1.c:8:5-8:10) + write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & h) (12-traces-min-rpb1.c:15:3-15:8) + read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & h) (12-traces-min-rpb1.c:27:3-27:26) + [Warning][Race] Memory location g (race with conf. 110): (12-traces-min-rpb1.c:7:5-7:10) + write with [lock:{A}, thread:[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:14:3-14:8) + read with [mhp:{created={[main, t_fun@12-traces-min-rpb1.c:25:3-25:40]}}, thread:[main]] (conf. 110) (exp: & g) (12-traces-min-rpb1.c:27:3-27:26) + [Info][Witness] witness generation summary: + total generation entries: 3 + [Info][Race] Memory locations race summary: + safe: 0 + vulnerable: 0 + unsafe: 2 + total memory locations: 2 + + $ yamlWitnessStrip < witness.yml + - entry_type: location_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 29 + column: 3 + function: main + location_invariant: + string: (0LL - (long long )g) + (long long )h == 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 19 + column: 3 + function: t_fun + location_invariant: + string: (0LL - (long long )g) + (long long )h == 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 12-traces-min-rpb1.c + file_hash: $FILE_HASH + line: 14 + column: 3 + function: t_fun + location_invariant: + string: (0LL - (long long )g) + (long long )h == 0LL + type: assertion + format: C diff --git a/tests/regression/36-apron/45-context.c b/tests/regression/36-apron/45-context.c index 94328af97f..eda945abd5 100644 --- a/tests/regression/36-apron/45-context.c +++ b/tests/regression/36-apron/45-context.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --enable ana.relation.context +// SKIP PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --enable ana.relation.context extern int __VERIFIER_nondet_int(); #include diff --git a/tests/regression/36-apron/46-no-context.c b/tests/regression/36-apron/46-no-context.c index bf115cee24..640784b913 100644 --- a/tests/regression/36-apron/46-no-context.c +++ b/tests/regression/36-apron/46-no-context.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --disable ana.relation.context +// SKIP PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --disable ana.relation.context extern int __VERIFIER_nondet_int(); #include diff --git a/tests/regression/36-apron/47-no-context-attribute.c b/tests/regression/36-apron/47-no-context-attribute.c index 90b58cdc28..cf111f5ffc 100644 --- a/tests/regression/36-apron/47-no-context-attribute.c +++ b/tests/regression/36-apron/47-no-context-attribute.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --enable ana.relation.context +// SKIP PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --enable ana.relation.context extern int __VERIFIER_nondet_int(); #include diff --git a/tests/regression/36-apron/48-context-attribute.c b/tests/regression/36-apron/48-context-attribute.c index 5e5ecf01fe..3304c20388 100644 --- a/tests/regression/36-apron/48-context-attribute.c +++ b/tests/regression/36-apron/48-context-attribute.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --disable ana.relation.context +// SKIP PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.path_sens[+] threadflag --enable ana.int.interval --disable ana.relation.context extern int __VERIFIER_nondet_int(); #include diff --git a/tests/regression/36-apron/52-queuesize.t b/tests/regression/36-apron/52-queuesize.t new file mode 100644 index 0000000000..62851f2ec9 --- /dev/null +++ b/tests/regression/36-apron/52-queuesize.t @@ -0,0 +1,281 @@ +`ana.apron.invariant.diff-box` test case from https://github.com/goblint/analyzer/pull/762. + +Without diff-box: + + $ goblint --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --disable witness.invariant.other --disable ana.base.invariant.enabled --set ana.relation.privatization mutex-meet --set ana.activated[+] apron --enable ana.sv-comp.functions --set ana.apron.domain polyhedra --enable ana.relation.invariant.one-var --disable ana.apron.invariant.diff-box 52-queuesize.c + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:67:5-67:31) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:68:5-68:38) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:69:5-69:31) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:70:5-70:38) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:71:5-71:45) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:15:3-15:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:16:3-16:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:17:3-17:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:18:3-18:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:19:3-19:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:26:3-26:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:27:3-27:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:28:3-28:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:29:3-29:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:30:3-30:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:36:3-36:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:37:3-37:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:38:3-38:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:39:3-39:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:40:3-40:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:47:3-47:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:48:3-48:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:49:3-49:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:50:3-50:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:51:3-51:43) + [Warning][Deadcode] Function 'worker' has dead code: + on line 58 (52-queuesize.c:58-58) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 53 + dead: 1 + total lines: 54 + [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: + total generation entries: 8 + [Info][Race] Memory locations race summary: + safe: 3 + vulnerable: 0 + unsafe: 0 + total memory locations: 3 + + $ yamlWitnessStrip < witness.yml | tee witness-disable-diff-box.yml + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: push + location_invariant: + string: 2147483647LL - (long long )capacity >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: push + location_invariant: + string: (long long )free >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: push + location_invariant: + string: (long long )capacity - (long long )free >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: push + location_invariant: + string: ((0LL - (long long )capacity) + (long long )free) + (long long )used == + 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 15 + column: 3 + function: pop + location_invariant: + string: 2147483647LL - (long long )capacity >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 15 + column: 3 + function: pop + location_invariant: + string: (long long )free >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 15 + column: 3 + function: pop + location_invariant: + string: (long long )capacity - (long long )free >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 15 + column: 3 + function: pop + location_invariant: + string: ((0LL - (long long )capacity) + (long long )free) + (long long )used == + 0LL + type: assertion + format: C + +With diff-box: + + $ goblint --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --disable witness.invariant.other --disable ana.base.invariant.enabled --set ana.relation.privatization mutex-meet --set ana.activated[+] apron --enable ana.sv-comp.functions --set ana.apron.domain polyhedra --enable ana.relation.invariant.one-var --enable ana.apron.invariant.diff-box 52-queuesize.c + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:67:5-67:31) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:68:5-68:38) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:69:5-69:31) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:70:5-70:38) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:71:5-71:45) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:15:3-15:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:16:3-16:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:17:3-17:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:18:3-18:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:19:3-19:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:26:3-26:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:27:3-27:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:28:3-28:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:29:3-29:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:30:3-30:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:36:3-36:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:37:3-37:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:38:3-38:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:39:3-39:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:40:3-40:43) + [Success][Assert] Assertion "free >= 0" will succeed (52-queuesize.c:47:3-47:29) + [Success][Assert] Assertion "free <= capacity" will succeed (52-queuesize.c:48:3-48:36) + [Success][Assert] Assertion "used >= 0" will succeed (52-queuesize.c:49:3-49:29) + [Success][Assert] Assertion "used <= capacity" will succeed (52-queuesize.c:50:3-50:36) + [Success][Assert] Assertion "used + free == capacity" will succeed (52-queuesize.c:51:3-51:43) + [Warning][Deadcode] Function 'worker' has dead code: + on line 58 (52-queuesize.c:58-58) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 53 + dead: 1 + total lines: 54 + [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: + total generation entries: 6 + [Info][Race] Memory locations race summary: + safe: 3 + vulnerable: 0 + unsafe: 0 + total memory locations: 3 + + $ yamlWitnessStrip < witness.yml | tee witness-enable-diff-box.yml + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: push + location_invariant: + string: (long long )free >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: push + location_invariant: + string: (long long )capacity - (long long )free >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: push + location_invariant: + string: ((0LL - (long long )capacity) + (long long )free) + (long long )used == + 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 15 + column: 3 + function: pop + location_invariant: + string: (long long )free >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 15 + column: 3 + function: pop + location_invariant: + string: (long long )capacity - (long long )free >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 15 + column: 3 + function: pop + location_invariant: + string: ((0LL - (long long )capacity) + (long long )free) + (long long )used == + 0LL + type: assertion + format: C + +Compare witnesses: + + $ yamlWitnessStripDiff witness-disable-diff-box.yml witness-enable-diff-box.yml + # Left-only entries: + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 36 + column: 3 + function: push + location_invariant: + string: 2147483647LL - (long long )capacity >= 0LL + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: 52-queuesize.c + file_hash: $FILE_HASH + line: 15 + column: 3 + function: pop + location_invariant: + string: 2147483647LL - (long long )capacity >= 0LL + type: assertion + format: C + --- + # Right-only entries: + [] diff --git a/tests/regression/36-apron/65-multi-lock-producer-consumer.c b/tests/regression/36-apron/65-multi-lock-producer-consumer.c index ae71c36dcf..e7041a09d3 100644 --- a/tests/regression/36-apron/65-multi-lock-producer-consumer.c +++ b/tests/regression/36-apron/65-multi-lock-producer-consumer.c @@ -1,5 +1,6 @@ // SKIP PARAM: --set ana.activated[+] apron --enable ana.sv-comp.functions --set ana.relation.privatization mutex-meet --set ana.apron.domain polyhedra // TODO: why doesn't mutex-meet-tid succeed? a widening loses some upper bound and we forget a possible overflow, succeeds with assume_none +// NOCHECK #include #include diff --git a/tests/regression/36-apron/86-branched-thread-creation.c b/tests/regression/36-apron/86-branched-thread-creation.c index cac0a881a6..91a5411e8f 100644 --- a/tests/regression/36-apron/86-branched-thread-creation.c +++ b/tests/regression/36-apron/86-branched-thread-creation.c @@ -40,7 +40,7 @@ int main(void) { if(!mt) { pthread_mutex_lock(&mutex); - __goblint_check(g==h); //MAYFAIL + __goblint_check(g==h); //FAIL pthread_mutex_unlock(&mutex); } diff --git a/tests/regression/36-apron/dune b/tests/regression/36-apron/dune index 6ea87f5ba8..42869053bc 100644 --- a/tests/regression/36-apron/dune +++ b/tests/regression/36-apron/dune @@ -8,3 +8,8 @@ (glob_files ??-*.c)) (locks /update_suite) (action (chdir ../../.. (run %{update_suite} group apron -q)))) + +(cram + (alias runaprontest) + (enabled_if %{lib-available:apron}) + (deps (glob_files *.c))) diff --git a/tests/regression/38-int-refinements/05-invalid-widen.c b/tests/regression/38-int-refinements/05-invalid-widen.c index 59d76019a8..ad39ff761c 100644 --- a/tests/regression/38-int-refinements/05-invalid-widen.c +++ b/tests/regression/38-int-refinements/05-invalid-widen.c @@ -1,4 +1,5 @@ //PARAM: --set ana.int.refinement once --enable ana.int.enums +// NOCRASH #include int main() { diff --git a/tests/regression/41-stdlib/03-noqsort.c b/tests/regression/41-stdlib/03-noqsort.c index e7a1de89a3..22672083d7 100644 --- a/tests/regression/41-stdlib/03-noqsort.c +++ b/tests/regression/41-stdlib/03-noqsort.c @@ -1,4 +1,5 @@ // PARAM: --set pre.cppflags[+] -DGOBLINT_NO_QSORT +// CRAM #include // There should be no CIL warning about multiple definitions here diff --git a/tests/regression/41-stdlib/03-noqsort.t b/tests/regression/41-stdlib/03-noqsort.t new file mode 100644 index 0000000000..cd56e0047d --- /dev/null +++ b/tests/regression/41-stdlib/03-noqsort.t @@ -0,0 +1,8 @@ +There should be no CIL warning about multiple definitions: + + $ goblint --set pre.cppflags[+] -DGOBLINT_NO_QSORT 03-noqsort.c + [Warning][Deadcode] Function 'qsort' is uncalled: 1 LLoC (03-noqsort.c:6:1-7:1) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 2 + dead: 1 (1 in uncalled functions) + total lines: 3 diff --git a/tests/regression/41-stdlib/dune b/tests/regression/41-stdlib/dune new file mode 100644 index 0000000000..23c0dd3290 --- /dev/null +++ b/tests/regression/41-stdlib/dune @@ -0,0 +1,2 @@ +(cram + (deps (glob_files *.c))) diff --git a/tests/regression/43-struct-domain/12-aget.c b/tests/regression/43-struct-domain/12-aget.c index 193e463418..6e67cefe75 100644 --- a/tests/regression/43-struct-domain/12-aget.c +++ b/tests/regression/43-struct-domain/12-aget.c @@ -1,5 +1,5 @@ // PARAM: --set ana.base.structs.domain "sets" - +// NOCHECK // This is shortened from aget.c in the bench repository // There was an issue when testing arrays, as they init to bottom and this code triggers narrowing. /* Generated by CIL v. 1.3.6 */ diff --git a/tests/regression/43-struct-domain/25-aget-keyed.c b/tests/regression/43-struct-domain/25-aget-keyed.c index 94d5495ae1..09c80c44cc 100644 --- a/tests/regression/43-struct-domain/25-aget-keyed.c +++ b/tests/regression/43-struct-domain/25-aget-keyed.c @@ -1,5 +1,5 @@ // PARAM: --set ana.base.structs.domain "keyed" - +// NOCHECK // This is shortened from aget.c in the bench repository // There was an issue when testing arrays, as they init to bottom and this code triggers narrowing. /* Generated by CIL v. 1.3.6 */ diff --git a/tests/regression/44-trier_analyzer/00-A0.c b/tests/regression/44-trier_analyzer/00-A0.c index defd7022f0..bfeb7c5509 100644 --- a/tests/regression/44-trier_analyzer/00-A0.c +++ b/tests/regression/44-trier_analyzer/00-A0.c @@ -1,3 +1,4 @@ +// NOCHECK extern int printf(char *, ...); main () { diff --git a/tests/regression/44-trier_analyzer/01-A1.c b/tests/regression/44-trier_analyzer/01-A1.c index a04fafc238..967b8d79d0 100644 --- a/tests/regression/44-trier_analyzer/01-A1.c +++ b/tests/regression/44-trier_analyzer/01-A1.c @@ -1,3 +1,4 @@ +// NOCHECK extern int printf (char *, ...); extern int scanf (char *, ...); diff --git a/tests/regression/44-trier_analyzer/02-abc.c b/tests/regression/44-trier_analyzer/02-abc.c index 6a23697a7b..f29d3787e7 100644 --- a/tests/regression/44-trier_analyzer/02-abc.c +++ b/tests/regression/44-trier_analyzer/02-abc.c @@ -1,3 +1,4 @@ +// NOCHECK #include #include diff --git a/tests/regression/44-trier_analyzer/03-break.c b/tests/regression/44-trier_analyzer/03-break.c index 4c87a8277f..78a8cf26d6 100644 --- a/tests/regression/44-trier_analyzer/03-break.c +++ b/tests/regression/44-trier_analyzer/03-break.c @@ -1,3 +1,4 @@ +// NOCHECK extern int printf(); main () { diff --git a/tests/regression/44-trier_analyzer/13-ifif.c b/tests/regression/44-trier_analyzer/13-ifif.c index 3e456a9576..e9025f1fa8 100644 --- a/tests/regression/44-trier_analyzer/13-ifif.c +++ b/tests/regression/44-trier_analyzer/13-ifif.c @@ -1,3 +1,4 @@ +// NOCHECK extern int printf(); extern int scanf(); diff --git a/tests/regression/44-trier_analyzer/15-P1.c b/tests/regression/44-trier_analyzer/15-P1.c index 2690a6da44..b169ea4876 100644 --- a/tests/regression/44-trier_analyzer/15-P1.c +++ b/tests/regression/44-trier_analyzer/15-P1.c @@ -1,3 +1,4 @@ +// NOCHECK main () { int y, a; int *p, *q, *t; diff --git a/tests/regression/44-trier_analyzer/16-P2.c b/tests/regression/44-trier_analyzer/16-P2.c index 3942760be7..2f27d5dbe0 100644 --- a/tests/regression/44-trier_analyzer/16-P2.c +++ b/tests/regression/44-trier_analyzer/16-P2.c @@ -1,3 +1,4 @@ +// NOCHECK main () { int y, a, b; int *p, *q; diff --git a/tests/regression/44-trier_analyzer/17-P3.c b/tests/regression/44-trier_analyzer/17-P3.c index 7b8c8a13d8..0bbc9ee2d2 100644 --- a/tests/regression/44-trier_analyzer/17-P3.c +++ b/tests/regression/44-trier_analyzer/17-P3.c @@ -1,3 +1,4 @@ +// NOCHECK #include extern void *malloc(size_t); diff --git a/tests/regression/44-trier_analyzer/23-rec0.c b/tests/regression/44-trier_analyzer/23-rec0.c index aa13bbac8e..ee43820bb1 100644 --- a/tests/regression/44-trier_analyzer/23-rec0.c +++ b/tests/regression/44-trier_analyzer/23-rec0.c @@ -1,3 +1,4 @@ +// NOCHECK extern int printf(char *, ...); extern int scanf(char *, ...); extern void exit(int); diff --git a/tests/regression/44-trier_analyzer/33-recA.c b/tests/regression/44-trier_analyzer/33-recA.c index 74fa54b768..2938403326 100644 --- a/tests/regression/44-trier_analyzer/33-recA.c +++ b/tests/regression/44-trier_analyzer/33-recA.c @@ -1,4 +1,5 @@ //PARAM: --enable ana.context.widen +// NOCHECK //Needs context widening even with only def_exc extern int scanf(char *, ...); extern int printf(char *, ...); diff --git a/tests/regression/46-apron2/03-other-assume.c b/tests/regression/46-apron2/03-other-assume.c index 635911a197..c136d1bde3 100644 --- a/tests/regression/46-apron2/03-other-assume.c +++ b/tests/regression/46-apron2/03-other-assume.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.activated[+] threadJoins --sets ana.relation.privatization mutex-meet-tid +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.activated[+] threadJoins --set ana.relation.privatization mutex-meet-tid #include #include diff --git a/tests/regression/46-apron2/04-other-assume-inprec.c b/tests/regression/46-apron2/04-other-assume-inprec.c index 09b8d150a8..ccf8ecefa1 100644 --- a/tests/regression/46-apron2/04-other-assume-inprec.c +++ b/tests/regression/46-apron2/04-other-assume-inprec.c @@ -1,4 +1,4 @@ -// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.activated[+] threadJoins --sets ana.relation.privatization mutex-meet-tid +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.activated[+] threadJoins --set ana.relation.privatization mutex-meet-tid #include #include diff --git a/tests/regression/46-apron2/26-autotune.c b/tests/regression/46-apron2/26-autotune.c index 96c5c85278..ec8f55fe19 100644 --- a/tests/regression/46-apron2/26-autotune.c +++ b/tests/regression/46-apron2/26-autotune.c @@ -1,4 +1,4 @@ -//SKIP PARAM: --enable ana.int.interval --sets sem.int.signed_overflow assume_none --set ana.activated[+] apron --enable ana.autotune.enabled +//SKIP PARAM: --enable ana.int.interval --set sem.int.signed_overflow assume_none --set ana.activated[+] apron --enable ana.autotune.enabled // Check that autotuner disables context for apron as well for recursive calls #include diff --git a/tests/regression/46-apron2/28-sv-comp-unroll-term.c b/tests/regression/46-apron2/28-sv-comp-unroll-term.c index a22b0e16bd..2a1f947dca 100644 --- a/tests/regression/46-apron2/28-sv-comp-unroll-term.c +++ b/tests/regression/46-apron2/28-sv-comp-unroll-term.c @@ -2,7 +2,7 @@ // Minimized from sv-benchmarks/c/ldv-linux-3.4-simple/32_1_cilled_ok_nondet_linux-3.4-32_1-drivers--staging--speakup--speakup_spkout.ko-ldv_main0_sequence_infinite_withcheck_stateful.cil.out.i // using loop unrolling of 1. -// Used to not terminate. +// NOTIMEOUT: Used to not terminate. struct speakup_info_t { int port_tts; diff --git a/tests/regression/46-apron2/30-autotune-stub.c b/tests/regression/46-apron2/30-autotune-stub.c index e1b7603c3b..02c80e7d21 100644 --- a/tests/regression/46-apron2/30-autotune-stub.c +++ b/tests/regression/46-apron2/30-autotune-stub.c @@ -1,4 +1,4 @@ -//SKIP PARAM: --enable ana.int.interval --sets sem.int.signed_overflow assume_none --set ana.activated[+] apron --enable ana.autotune.enabled +//SKIP PARAM: --enable ana.int.interval --set sem.int.signed_overflow assume_none --set ana.activated[+] apron --enable ana.autotune.enabled // Check that autotuner respect goblint_stub attributes as hints to not track variables. #include diff --git a/tests/regression/46-apron2/34-iset_previously_problematic_c.c b/tests/regression/46-apron2/34-iset_previously_problematic_c.c index 3c1847bbcf..2b0f526957 100644 --- a/tests/regression/46-apron2/34-iset_previously_problematic_c.c +++ b/tests/regression/46-apron2/34-iset_previously_problematic_c.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int i = 0; diff --git a/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c b/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c index 4cbcaef064..89253052d5 100644 --- a/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c +++ b/tests/regression/46-apron2/36-iset_previosuly_problematic_g.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int i = 0; diff --git a/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c b/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c index 918e67b0e8..c88aabe377 100644 --- a/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c +++ b/tests/regression/46-apron2/37-iset_previosuly_problematic_f.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int a[256]; diff --git a/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c b/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c index 18a53d31aa..480691409d 100644 --- a/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c +++ b/tests/regression/46-apron2/39-iset_previosuly_problematic_d.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int l; diff --git a/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c b/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c index dab6078b98..b9506cb453 100644 --- a/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c +++ b/tests/regression/46-apron2/45-iset_previosuly_problematic_i.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK char buf2[67]; int main(int argc, char **argv) diff --git a/tests/regression/46-apron2/47-iset_previously_problematic_a.c b/tests/regression/46-apron2/47-iset_previously_problematic_a.c index 2bf6044560..15d61d436b 100644 --- a/tests/regression/46-apron2/47-iset_previously_problematic_a.c +++ b/tests/regression/46-apron2/47-iset_previously_problematic_a.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int top; diff --git a/tests/regression/46-apron2/49-iset_previously_problematic_b.c b/tests/regression/46-apron2/49-iset_previously_problematic_b.c index 19d925013c..336efa4eb0 100644 --- a/tests/regression/46-apron2/49-iset_previously_problematic_b.c +++ b/tests/regression/46-apron2/49-iset_previously_problematic_b.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK typedef int wchar_t; typedef unsigned long size_t; diff --git a/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c b/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c index 551bb731e9..582cd4916c 100644 --- a/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c +++ b/tests/regression/46-apron2/51-iset_previosuly_problematic_e.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int j = -1; diff --git a/tests/regression/46-apron2/53-iset_previously_problematic_h.c b/tests/regression/46-apron2/53-iset_previously_problematic_h.c index 05489e47dc..242f1fb377 100644 --- a/tests/regression/46-apron2/53-iset_previously_problematic_h.c +++ b/tests/regression/46-apron2/53-iset_previously_problematic_h.c @@ -1,6 +1,7 @@ // SKIP PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] apron // These examples were cases were we saw issues of not reaching a fixpoint during development of the octagon domain. Since those issues might // resurface, these tests without asserts are included +// NOCHECK int main(int argc, char const *argv[]) { int iter = 0; diff --git a/tests/regression/46-apron2/58-issue-1249.c b/tests/regression/46-apron2/58-issue-1249.c index 862c539d15..903eafdac8 100644 --- a/tests/regression/46-apron2/58-issue-1249.c +++ b/tests/regression/46-apron2/58-issue-1249.c @@ -2,7 +2,7 @@ int *a; int b; void c(int d) { - // *a is a null pointer here, so we should warn but maybe not crash + // NOCRASH: *a is a null pointer here, so we should warn but maybe not crash *a = d; } int main() { diff --git a/tests/regression/46-apron2/59-issue-1319.c b/tests/regression/46-apron2/59-issue-1319.c index 1c11d6093e..62520c95c1 100644 --- a/tests/regression/46-apron2/59-issue-1319.c +++ b/tests/regression/46-apron2/59-issue-1319.c @@ -8,7 +8,7 @@ int main() t = &c; - // Type of *t and c do not match, this caused a crash before + // NOCRASH: Type of *t and c do not match, this caused a crash before if(*t == 'a') { t++; } @@ -18,7 +18,7 @@ int main() int other() { - // Same problem, but a bit more involved + // NOCRASH: Same problem, but a bit more involved unsigned char *t; char buf[100] = "bliblablubapk\r"; diff --git a/tests/regression/46-apron2/60-issue-1338.c b/tests/regression/46-apron2/60-issue-1338.c index 899fe613b3..92afa3b74f 100644 --- a/tests/regression/46-apron2/60-issue-1338.c +++ b/tests/regression/46-apron2/60-issue-1338.c @@ -1,4 +1,5 @@ // SKIP PARAM: --set ana.activated[+] apron +// NOCRASH #include int main() { diff --git a/tests/regression/46-apron2/82-fixpoint-not-reached.c b/tests/regression/46-apron2/82-fixpoint-not-reached.c index 1dff575a7c..66f00770da 100644 --- a/tests/regression/46-apron2/82-fixpoint-not-reached.c +++ b/tests/regression/46-apron2/82-fixpoint-not-reached.c @@ -1,5 +1,5 @@ // SKIP PARAM: --set sem.int.signed_overflow assume_none --set ana.activated[+] apron - +// FIXPOINT int main() { int minInt = -2147483647 + -1; int x = (minInt + -1) +1; diff --git a/tests/regression/46-apron2/90-malloc.c b/tests/regression/46-apron2/90-malloc.c new file mode 100644 index 0000000000..8780568748 --- /dev/null +++ b/tests/regression/46-apron2/90-malloc.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet --set ana.apron.domain interval --set sem.int.signed_overflow assume_none +// Checks that assinging to malloc'ed memory does not cause both branches to be dead +#include +#include +void nop(void* arg) { +} + +void main() { + pthread_t thread; + pthread_create(&thread, 0, &nop, 0); + + long *k = malloc(sizeof(long)); + *k = 5; + if (1) + ; + + __goblint_check(*k >= 5); // Reachable and true + + *k = *k+1; + __goblint_check(*k >= 5); // Reachable and true +} diff --git a/tests/regression/46-apron2/91-malloc-tid.c b/tests/regression/46-apron2/91-malloc-tid.c new file mode 100644 index 0000000000..36696956e7 --- /dev/null +++ b/tests/regression/46-apron2/91-malloc-tid.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid --set ana.apron.domain interval --set sem.int.signed_overflow assume_none +// Checks that assinging to malloc'ed memory does not cause both branches to be dead +#include +#include +void nop(void* arg) { +} + +void main() { + pthread_t thread; + pthread_create(&thread, 0, &nop, 0); + + long *k = malloc(sizeof(long)); + *k = 5; + if (1) + ; + + __goblint_check(*k >= 5); // Reachable and true + + *k = *k+1; + __goblint_check(*k >= 5); // Reachable and true +} diff --git a/tests/regression/46-apron2/92-malloc-atomic.c b/tests/regression/46-apron2/92-malloc-atomic.c new file mode 100644 index 0000000000..b2f057f6a4 --- /dev/null +++ b/tests/regression/46-apron2/92-malloc-atomic.c @@ -0,0 +1,21 @@ +// SKIP PARAM: --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-atomic --set ana.apron.domain interval --set sem.int.signed_overflow assume_none +// Checks that assinging to malloc'ed memory does not cause both branches to be dead +#include +#include +void nop(void* arg) { +} + +void main() { + pthread_t thread; + pthread_create(&thread, 0, &nop, 0); + + long *k = malloc(sizeof(long)); + *k = 5; + if (1) + ; + + __goblint_check(*k >= 5); // Reachable and true + + *k = *k+1; + __goblint_check(*k >= 5); // Reachable and true +} diff --git a/tests/regression/52-apron-mukherjee/19-mukherjee_fig_3_11.c b/tests/regression/52-apron-mukherjee/19-mukherjee_fig_3_11.c index b00a2b2270..73499179c5 100644 --- a/tests/regression/52-apron-mukherjee/19-mukherjee_fig_3_11.c +++ b/tests/regression/52-apron-mukherjee/19-mukherjee_fig_3_11.c @@ -1,5 +1,5 @@ // SKIP PARAM: --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.activated[+] threadJoins -// TODO: checks nothing? +// NOCHECK: checks nothing? #include int x; diff --git a/tests/regression/54-unroll_arrays/03-large_index_type.c b/tests/regression/54-unroll_arrays/03-large_index_type.c index 91c8056b32..80851aa4b8 100644 --- a/tests/regression/54-unroll_arrays/03-large_index_type.c +++ b/tests/regression/54-unroll_arrays/03-large_index_type.c @@ -1,6 +1,6 @@ //PARAM: --set ana.base.arrays.domain unroll --set ana.base.arrays.unrolling-factor 5 //from sv-comp test c/aws-c-common/memset_using_uint64_harness.i - +// NOCRASH typedef long unsigned int size_t; int main() { diff --git a/tests/regression/54-unroll_arrays/04-access_no_bounds.c b/tests/regression/54-unroll_arrays/04-access_no_bounds.c index 70a2726045..bd4fca4b68 100644 --- a/tests/regression/54-unroll_arrays/04-access_no_bounds.c +++ b/tests/regression/54-unroll_arrays/04-access_no_bounds.c @@ -1,6 +1,6 @@ // PARAM: --set ana.base.arrays.domain unroll --set ana.base.arrays.unrolling-factor 5 //from sv-comp test c/aws-c-common/aws_array_eq_c_str_ignore_case_harness.i - +// NOCRASH typedef long unsigned int size_t; typedef unsigned char uint8_t; diff --git a/tests/regression/55-loop-unrolling/08-bad.c b/tests/regression/55-loop-unrolling/08-bad.c index e2ad0f0453..df9131d6bf 100644 --- a/tests/regression/55-loop-unrolling/08-bad.c +++ b/tests/regression/55-loop-unrolling/08-bad.c @@ -1,4 +1,5 @@ // PARAM: --set exp.unrolling-factor 1 --enable dbg.run_cil_check +// CRAM int main() { int m; diff --git a/tests/regression/55-loop-unrolling/08-bad.t b/tests/regression/55-loop-unrolling/08-bad.t index 11cded728f..a8d8d62522 100644 --- a/tests/regression/55-loop-unrolling/08-bad.t +++ b/tests/regression/55-loop-unrolling/08-bad.t @@ -1,6 +1,4 @@ $ goblint --set lib.activated '[]' --set exp.unrolling-factor 1 --enable justcil --set dbg.justcil-printer clean 08-bad.c - [Info] unrolling loop at 08-bad.c:8:7-8:23 with factor 1 - [Info] unrolling loop at 08-bad.c:14:8-14:24 with factor 1 int main(void) { int m ; @@ -8,11 +6,6 @@ { { goto switch_default; - { - if (! 0) { - goto loop_end; - } - loop_continue_0: /* CIL Label */ ; switch_default: /* CIL Label */ { while (1) { @@ -23,16 +16,9 @@ } while_break: /* CIL Label */ ; } - loop_end: /* CIL Label */ ; - } switch_break: /* CIL Label */ ; } goto lab; - { - if (! 0) { - goto loop_end___0; - } - loop_continue_0___0: /* CIL Label */ ; lab: { while (1) { @@ -43,8 +29,6 @@ } while_break___0: /* CIL Label */ ; } - loop_end___0: /* CIL Label */ ; - } return (0); } } diff --git a/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.c b/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.c index 096c031cf1..4a3f7c1cfa 100644 --- a/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.c +++ b/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.c @@ -1,3 +1,4 @@ +// CRAM int main() { int i = 0; while (i < 10) diff --git a/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t b/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t index 32ec663717..3a3b7c43cf 100644 --- a/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t +++ b/tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t @@ -1,7 +1,7 @@ $ cfgDot --unroll 1 11-unrolled-loop-invariant.c - [Info] unrolling loop at 11-unrolled-loop-invariant.c:3:3-4:8 with factor 1 - [Info] unrolling loop at 11-unrolled-loop-invariant.c:8:5-9:10 with factor 1 - [Info] unrolling loop at 11-unrolled-loop-invariant.c:7:3-11:3 with factor 1 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:4:3-5:8 with factor 1 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:9:5-10:10 with factor 1 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:8:3-12:3 with factor 1 $ graph-easy --as=boxart main.dot ┌──────────────────────────────────────────────────────┐ @@ -11,160 +11,160 @@ │ (body) ▼ ┌──────────────────────────────────────────────────────┐ - │ 11-unrolled-loop-invariant.c:2:3-2:12 │ - │ (11-unrolled-loop-invariant.c:2:7-2:12 (synthetic)) │ - │ YAML loc: 11-unrolled-loop-invariant.c:2:3-2:12 │ + │ 11-unrolled-loop-invariant.c:3:3-3:12 │ + │ (11-unrolled-loop-invariant.c:3:7-3:12 (synthetic)) │ + │ YAML loc: 11-unrolled-loop-invariant.c:3:3-3:12 │ │ GraphML: true; server: false │ └──────────────────────────────────────────────────────┘ │ │ i = 0 ▼ ┌──────────────────────────────────────────────────────┐ - │ 11-unrolled-loop-invariant.c:3:3-4:8 (synthetic) │ - │ (11-unrolled-loop-invariant.c:3:10-3:16 (synthetic)) │ - │ YAML loop: 11-unrolled-loop-invariant.c:3:3-4:8 │ + │ 11-unrolled-loop-invariant.c:4:3-5:8 (synthetic) │ + │ (11-unrolled-loop-invariant.c:4:10-4:16 (synthetic)) │ + │ YAML loop: 11-unrolled-loop-invariant.c:4:3-5:8 │ │ GraphML: true; server: false │ - ┌───────────── │ loop: 11-unrolled-loop-invariant.c:3:3-4:8 │ ·┐ + ┌───────────── │ loop: 11-unrolled-loop-invariant.c:4:3-5:8 │ ·┐ │ └──────────────────────────────────────────────────────┘ : │ │ : │ │ Pos(i < 10) : │ ▼ : │ ┌──────────────────────────────────────────────────────┐ : - │ │ 11-unrolled-loop-invariant.c:4:5-4:8 │ : - │ │ (11-unrolled-loop-invariant.c:4:5-4:8) │ : - │ │ YAML loc: 11-unrolled-loop-invariant.c:4:5-4:8 │ : + │ │ 11-unrolled-loop-invariant.c:5:5-5:8 │ : + │ │ (11-unrolled-loop-invariant.c:5:5-5:8) │ : + │ │ YAML loc: 11-unrolled-loop-invariant.c:5:5-5:8 │ : │ │ GraphML: true; server: true │ ·┼····································································┐ │ └──────────────────────────────────────────────────────┘ : : │ │ : : │ │ i = i + 1 : : │ Neg(i < 10) ▼ ▼ : │ ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ : - │ │ 11-unrolled-loop-invariant.c:3:3-4:8 (synthetic) │ : - │ │ (11-unrolled-loop-invariant.c:3:10-3:16 (synthetic)) │ : - │ │ YAML loop: 11-unrolled-loop-invariant.c:3:3-4:8 │ : + │ │ 11-unrolled-loop-invariant.c:4:3-5:8 (synthetic) │ : + │ │ (11-unrolled-loop-invariant.c:4:10-4:16 (synthetic)) │ : + │ │ YAML loop: 11-unrolled-loop-invariant.c:4:3-5:8 │ : │ │ GraphML: true; server: false │ : - │ │ loop: 11-unrolled-loop-invariant.c:3:3-4:8 │ : + │ │ loop: 11-unrolled-loop-invariant.c:4:3-5:8 │ : │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ : │ │ │ ▲ : │ │ Neg(i < 10) │ Pos(i < 10) │ i = i + 1 : │ ▼ │ │ : │ ┌──────────────────────────────────────────────────────┐ │ ┌───────────────────────────────────────────────────┐ : - │ │ 11-unrolled-loop-invariant.c:6:3-6:19 │ │ │ 11-unrolled-loop-invariant.c:4:5-4:8 │ : - │ │ (11-unrolled-loop-invariant.c:6:7-6:12 (synthetic)) │ │ │ (11-unrolled-loop-invariant.c:4:5-4:8) │ : - │ │ YAML loc: 11-unrolled-loop-invariant.c:6:3-6:19 │ │ │ YAML loc: 11-unrolled-loop-invariant.c:4:5-4:8 │ : + │ │ 11-unrolled-loop-invariant.c:7:3-7:19 │ │ │ 11-unrolled-loop-invariant.c:5:5-5:8 │ : + │ │ (11-unrolled-loop-invariant.c:7:7-7:12 (synthetic)) │ │ │ (11-unrolled-loop-invariant.c:5:5-5:8) │ : + │ │ YAML loc: 11-unrolled-loop-invariant.c:7:3-7:19 │ │ │ YAML loc: 11-unrolled-loop-invariant.c:5:5-5:8 │ : └────────────▶ │ GraphML: true; server: false │ └───────────▶ │ GraphML: true; server: true │ ◀┘ └──────────────────────────────────────────────────────┘ └───────────────────────────────────────────────────┘ │ │ j = 0 ▼ ┌──────────────────────────────────────────────────────┐ - │ 11-unrolled-loop-invariant.c:6:3-6:19 (synthetic) │ - │ (11-unrolled-loop-invariant.c:6:14-6:19 (synthetic)) │ + │ 11-unrolled-loop-invariant.c:7:3-7:19 (synthetic) │ + │ (11-unrolled-loop-invariant.c:7:14-7:19 (synthetic)) │ │ GraphML: true; server: false │ └──────────────────────────────────────────────────────┘ │ │ k = 0 ▼ ┌──────────────────────────────────────────────────────┐ ┌───────────────────────────────────────────────────┐ ┌──────────────────┐ - │ 11-unrolled-loop-invariant.c:7:3-11:3 (synthetic) │ │ 11-unrolled-loop-invariant.c:12:3-12:11 │ │ │ - │ (11-unrolled-loop-invariant.c:7:10-7:16 (synthetic)) │ │ (11-unrolled-loop-invariant.c:12:10-12:11) │ │ │ - │ YAML loop: 11-unrolled-loop-invariant.c:7:3-11:3 │ │ YAML loc: 11-unrolled-loop-invariant.c:12:3-12:11 │ │ return of main() │ + │ 11-unrolled-loop-invariant.c:8:3-12:3 (synthetic) │ │ 11-unrolled-loop-invariant.c:13:3-13:11 │ │ │ + │ (11-unrolled-loop-invariant.c:8:10-8:16 (synthetic)) │ │ (11-unrolled-loop-invariant.c:13:10-13:11) │ │ │ + │ YAML loop: 11-unrolled-loop-invariant.c:8:3-12:3 │ │ YAML loc: 11-unrolled-loop-invariant.c:13:3-13:11 │ │ return of main() │ │ GraphML: true; server: false │ Neg(j < 10) │ GraphML: true; server: true │ return 0 │ │ - ┌············· │ loop: 11-unrolled-loop-invariant.c:7:3-11:3 │ ─────────────▶ │ │ ──────────▶ │ │ + ┌············· │ loop: 11-unrolled-loop-invariant.c:8:3-12:3 │ ─────────────▶ │ │ ──────────▶ │ │ : └──────────────────────────────────────────────────────┘ └───────────────────────────────────────────────────┘ └──────────────────┘ : │ ▲ Neg(j < 10) : │ Pos(j < 10) └──────────────────────────────────────────────────────────────────────────────────────────┐ : ▼ │ : ┌──────────────────────────────────────────────────────┐ │ - : │ 11-unrolled-loop-invariant.c:8:5-9:10 (synthetic) │ │ - : │ (11-unrolled-loop-invariant.c:8:12-8:19 (synthetic)) │ │ - : │ YAML loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ │ + : │ 11-unrolled-loop-invariant.c:9:5-10:10 (synthetic) │ │ + : │ (11-unrolled-loop-invariant.c:9:12-9:19 (synthetic)) │ │ + : │ YAML loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ │ : │ GraphML: true; server: false │ │ - ┌──────────────────────────┼───────────── │ loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ ······································································┐ │ + ┌──────────────────────────┼───────────── │ loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ ······································································┐ │ │ : └──────────────────────────────────────────────────────┘ : │ │ : │ : │ │ : │ Pos(k < 100) : │ │ : ▼ : │ │ : ┌──────────────────────────────────────────────────────┐ : │ - │ : │ 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ - │ : │ (11-unrolled-loop-invariant.c:9:7-9:10) │ : │ - │ : │ YAML loc: 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ + │ : │ 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ + │ : │ (11-unrolled-loop-invariant.c:10:7-10:10) │ : │ + │ : │ YAML loc: 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ │ : │ GraphML: true; server: true │ ······································································┼············┐ │ │ : └──────────────────────────────────────────────────────┘ : : │ │ : │ : : │ │ : │ k = k + 1 ┌────────────────────────────────────────────────────┼────────────┼────────────────────────┼─────────────┐ │ : ▼ │ : : │ │ │ : ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ : : │ │ - │ : │ 11-unrolled-loop-invariant.c:8:5-9:10 (synthetic) │ : : │ │ - │ : │ (11-unrolled-loop-invariant.c:8:12-8:19 (synthetic)) │ : : │ │ - │ : │ YAML loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ : : │ │ + │ : │ 11-unrolled-loop-invariant.c:9:5-10:10 (synthetic) │ : : │ │ + │ : │ (11-unrolled-loop-invariant.c:9:12-9:19 (synthetic)) │ : : │ │ + │ : │ YAML loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ : : │ │ │ : │ GraphML: true; server: false │ : : │ │ - │ : │ loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ : : │ │ + │ : │ loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ : : │ │ │ : └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ : : │ │ │ : │ : ▲ : : │ │ │ : │ Pos(k < 100) : │ k = k + 1 : : │ │ │ : ▼ : │ : : │ │ │ : ┌──────────────────────────────────────────────────────┐ : │ : : │ │ - │ : │ 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ : : │ │ - │ : │ (11-unrolled-loop-invariant.c:9:7-9:10) │ : │ : : │ │ - │ : │ YAML loc: 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ : : │ │ + │ : │ 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ : : │ │ + │ : │ (11-unrolled-loop-invariant.c:10:7-10:10) │ : │ : : │ │ + │ : │ YAML loc: 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ : : │ │ │ : │ GraphML: true; server: true │ ─┼───────────────┘ : : │ │ │ : └──────────────────────────────────────────────────────┘ : : : │ │ │ : : : : : │ │ ┌────┘ : : : : : │ │ │ : ▼ : : : │ │ │ : ┌──────────────────────────────────────────────────────┐ : : : │ │ - │ : │ 11-unrolled-loop-invariant.c:9:7-9:10 │ : : : │ │ - │ : │ (11-unrolled-loop-invariant.c:9:7-9:10) │ : : : │ │ - │ : │ YAML loc: 11-unrolled-loop-invariant.c:9:7-9:10 │ : : : │ │ + │ : │ 11-unrolled-loop-invariant.c:10:7-10:10 │ : : : │ │ + │ : │ (11-unrolled-loop-invariant.c:10:7-10:10) │ : : : │ │ + │ : │ YAML loc: 11-unrolled-loop-invariant.c:10:7-10:10 │ : : : │ │ │ ┌··························┼············▶ │ GraphML: true; server: true │ ◀┼───────────────┐ : : │ │ │ : : └──────────────────────────────────────────────────────┘ : │ : : │ │ │ : : │ : │ : : │ │ │ : : │ k = k + 1 : │ Pos(k < 100) : : │ │ │ : : ▼ ▼ │ : : │ │ │ : : ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ : : │ │ - │ : : │ 11-unrolled-loop-invariant.c:8:5-9:10 (synthetic) │ : : │ │ - │ : : │ (11-unrolled-loop-invariant.c:8:12-8:19 (synthetic)) │ : : │ │ - │ : : │ YAML loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ : : │ │ + │ : : │ 11-unrolled-loop-invariant.c:9:5-10:10 (synthetic) │ : : │ │ + │ : : │ (11-unrolled-loop-invariant.c:9:12-9:19 (synthetic)) │ : : │ │ + │ : : │ YAML loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ : : │ │ │ : k = k + 1 : │ GraphML: true; server: false │ : : │ │ - │ : ┌─────────────────────┼────────────▶ │ loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ ◀┼············┼···················┐ │ │ + │ : ┌─────────────────────┼────────────▶ │ loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ ◀┼············┼···················┐ │ │ │ : │ : └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ : : : │ │ │ : │ : │ : : : │ │ │ : │ : │ Neg(k < 100) : : : │ │ │ : │ : ▼ : : : │ │ │ : │ : ┌──────────────────────────────────────────────────────┐ : : : │ │ - │ : │ : │ 11-unrolled-loop-invariant.c:10:5-10:8 │ : : : │ │ - │ : │ : │ (11-unrolled-loop-invariant.c:10:5-10:8) │ : : : │ │ - │ : │ : │ YAML loc: 11-unrolled-loop-invariant.c:10:5-10:8 │ : : : │ │ + │ : │ : │ 11-unrolled-loop-invariant.c:11:5-11:8 │ : : : │ │ + │ : │ : │ (11-unrolled-loop-invariant.c:11:5-11:8) │ : : : │ │ + │ : │ : │ YAML loc: 11-unrolled-loop-invariant.c:11:5-11:8 │ : : : │ │ │ : │ ┌────────────────┼────────────▶ │ GraphML: true; server: true │ ◀·····································································┼············┼···················┼····┼·············┼····┐ │ : │ │ : └──────────────────────────────────────────────────────┘ : : : │ │ : │ : │ │ : │ : : : │ │ : │ : │ │ : │ j = j + 1 ┌────────────────────────────────────────────────────┼────────────┼───────────────────┼────┘ │ : │ : │ │ : ▼ │ : : : │ : │ : │ │ : ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ : : : │ : - │ : │ │ : │ 11-unrolled-loop-invariant.c:7:3-11:3 (synthetic) │ : : : │ : - │ : │ │ : │ (11-unrolled-loop-invariant.c:7:10-7:16 (synthetic)) │ : : : │ : - │ : │ │ Neg(k < 100) : │ YAML loop: 11-unrolled-loop-invariant.c:7:3-11:3 │ : : : │ : + │ : │ │ : │ 11-unrolled-loop-invariant.c:8:3-12:3 (synthetic) │ : : : │ : + │ : │ │ : │ (11-unrolled-loop-invariant.c:8:10-8:16 (synthetic)) │ : : : │ : + │ : │ │ Neg(k < 100) : │ YAML loop: 11-unrolled-loop-invariant.c:8:3-12:3 │ : : : │ : │ : │ │ : │ GraphML: true; server: false │ : : : │ : - │ : │ │ └············▶ │ loop: 11-unrolled-loop-invariant.c:7:3-11:3 │ ◀┼────────────┼───────────────────┼────┐ │ : + │ : │ │ └············▶ │ loop: 11-unrolled-loop-invariant.c:8:3-12:3 │ ◀┼────────────┼───────────────────┼────┐ │ : │ : │ │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ : : : │ │ : │ : │ │ │ : : : │ │ : │ : │ │ │ Pos(j < 10) : : : │ │ : │ : │ │ ▼ : : : │ │ : │ : │ │ ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ : : : │ │ : - │ : │ │ │ 11-unrolled-loop-invariant.c:8:5-9:10 (synthetic) │ : : : │ │ : - │ : │ │ │ (11-unrolled-loop-invariant.c:8:12-8:19 (synthetic)) │ : : : │ │ : - │ : │ │ │ YAML loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ : : : │ j = j + 1 │ : + │ : │ │ │ 11-unrolled-loop-invariant.c:9:5-10:10 (synthetic) │ : : : │ │ : + │ : │ │ │ (11-unrolled-loop-invariant.c:9:12-9:19 (synthetic)) │ : : : │ │ : + │ : │ │ │ YAML loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ : : : │ j = j + 1 │ : │ : │ │ │ GraphML: true; server: false │ : : : │ │ : - │ : │ └────────────────────────────── │ loop: 11-unrolled-loop-invariant.c:8:5-9:10 │ ◀┘ : : │ │ : + │ : │ └────────────────────────────── │ loop: 11-unrolled-loop-invariant.c:9:5-10:10 │ ◀┘ : : │ │ : │ : │ └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ : : │ │ : │ : │ │ : : : │ │ : │ : │ │ Pos(k < 100) └·································································┼···················┘ │ │ : │ : │ ▼ : │ │ : │ : │ ┌──────────────────────────────────────────────────────┐ : │ │ : - │ : │ │ 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ │ : - │ : │ │ (11-unrolled-loop-invariant.c:9:7-9:10) │ : │ │ : - │ : │ │ YAML loc: 11-unrolled-loop-invariant.c:9:7-9:10 │ : │ │ : + │ : │ │ 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ │ : + │ : │ │ (11-unrolled-loop-invariant.c:10:7-10:10) │ : │ │ : + │ : │ │ YAML loc: 11-unrolled-loop-invariant.c:10:7-10:10 │ : │ │ : │ : └─────────────────────────────────── │ GraphML: true; server: true │ ◀··················································································┘ ┌····┼·············┼····┘ │ : └──────────────────────────────────────────────────────┘ : │ │ │ : : : │ │ @@ -174,42 +174,42 @@ │ ┌·····························································································································································┘ │ │ │ : │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ - │ │ 11-unrolled-loop-invariant.c:10:5-10:8 │ │ │ - │ │ (11-unrolled-loop-invariant.c:10:5-10:8) │ │ │ - │ Neg(k < 100) │ YAML loc: 11-unrolled-loop-invariant.c:10:5-10:8 │ │ │ + │ │ 11-unrolled-loop-invariant.c:11:5-11:8 │ │ │ + │ │ (11-unrolled-loop-invariant.c:11:5-11:8) │ │ │ + │ Neg(k < 100) │ YAML loc: 11-unrolled-loop-invariant.c:11:5-11:8 │ │ │ └────────────────────────────────────────────▶ │ GraphML: true; server: true │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────┘ │ ▲ Neg(k < 100) │ └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ $ goblint --set lib.activated '[]' --set exp.unrolling-factor 5 --enable ana.int.interval --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant", "loop_invariant"]' 11-unrolled-loop-invariant.c - [Info] unrolling loop at 11-unrolled-loop-invariant.c:3:3-4:8 with factor 5 - [Info] unrolling loop at 11-unrolled-loop-invariant.c:8:5-9:10 with factor 5 - [Info] unrolling loop at 11-unrolled-loop-invariant.c:7:3-11:3 with factor 5 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:4:3-5:8 with factor 5 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:9:5-10:10 with factor 5 + [Info] unrolling loop at 11-unrolled-loop-invariant.c:8:3-12:3 with factor 5 [Info][Deadcode] Logical lines of code (LLoC) summary: live: 10 dead: 0 total lines: 10 - [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:7:10-7:16) - [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:7:10-7:16) - [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:3:10-3:16) - [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:3:10-3:16) - [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:3:10-3:16) - [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:3:10-3:16) - [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:3:10-3:16) - [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:7:10-7:16) - [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:7:10-7:16) - [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:12-8:19) - [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:7:10-7:16) + [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) + [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) + [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:4:10-4:16) + [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:4:10-4:16) + [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:4:10-4:16) + [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:4:10-4:16) + [Warning][Deadcode][CWE-571] condition 'i < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:4:10-4:16) + [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) + [Warning][Deadcode][CWE-570] condition 'k < 100' (possibly inserted by CIL) is always false (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) + [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'k < 100' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:9:12-9:19) + [Warning][Deadcode][CWE-571] condition 'j < 10' (possibly inserted by CIL) is always true (11-unrolled-loop-invariant.c:8:10-8:16) [Info][Witness] witness generation summary: total generation entries: 16 @@ -218,7 +218,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 8 + line: 9 column: 5 function: main loop_invariant: @@ -229,7 +229,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 8 + line: 9 column: 5 function: main loop_invariant: @@ -243,7 +243,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 7 + line: 8 column: 3 function: main loop_invariant: @@ -254,7 +254,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 7 + line: 8 column: 3 function: main loop_invariant: @@ -266,7 +266,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 3 + line: 4 column: 3 function: main loop_invariant: @@ -278,7 +278,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 12 + line: 13 column: 3 function: main location_invariant: @@ -289,7 +289,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 12 + line: 13 column: 3 function: main location_invariant: @@ -300,7 +300,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 12 + line: 13 column: 3 function: main location_invariant: @@ -311,7 +311,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 10 + line: 11 column: 5 function: main location_invariant: @@ -322,7 +322,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 10 + line: 11 column: 5 function: main location_invariant: @@ -333,7 +333,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 10 + line: 11 column: 5 function: main location_invariant: @@ -345,7 +345,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 9 + line: 10 column: 7 function: main location_invariant: @@ -356,7 +356,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 9 + line: 10 column: 7 function: main location_invariant: @@ -367,7 +367,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 9 + line: 10 column: 7 function: main location_invariant: @@ -379,7 +379,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 6 + line: 7 column: 3 function: main location_invariant: @@ -390,7 +390,7 @@ location: file_name: 11-unrolled-loop-invariant.c file_hash: $FILE_HASH - line: 4 + line: 5 column: 5 function: main location_invariant: diff --git a/tests/regression/55-loop-unrolling/12-loop-no-overflows.c b/tests/regression/55-loop-unrolling/12-loop-no-overflows.c new file mode 100644 index 0000000000..03554e06c5 --- /dev/null +++ b/tests/regression/55-loop-unrolling/12-loop-no-overflows.c @@ -0,0 +1,37 @@ +// PARAM: --enable ana.int.interval_set +// extracted from SV-COMP task ldv-memsafety/memleaks_test12-2.i + +typedef unsigned int size_t; +struct ldv_list_head { + struct ldv_list_head *next, *prev; +}; +struct ldv_list_head ldv_global_msg_list = {&(ldv_global_msg_list), &(ldv_global_msg_list)}; +struct ldv_msg { + void *data; + struct ldv_list_head list; +}; + +static inline void __ldv_list_del(struct ldv_list_head *prev, struct ldv_list_head *next) { + next->prev = prev; + prev->next = next; +} + +static inline void ldv_list_del(struct ldv_list_head *entry) { + __ldv_list_del(entry->prev, entry->next); +} + +void ldv_msg_free(struct ldv_msg *msg) { + free(msg->data); + free(msg); +} + +// ldv_destroy_msgs +void main(void) { + struct ldv_msg *msg; + struct ldv_msg *n; + for (msg = ({ const typeof( ((typeof(*msg) *)0)->list ) *__mptr = ((&ldv_global_msg_list)->next); (typeof(*msg) *)( (char *)__mptr - ((size_t) &((typeof(*msg) *)0)->list) ); }), n = ({ const typeof( ((typeof(*(msg)) *)0)->list ) *__mptr = ((msg)->list.next); (typeof(*(msg)) *)( (char *)__mptr - ((size_t) &((typeof(*(msg)) *)0)->list) ); }); &msg->list != (&ldv_global_msg_list); msg = n, n = ({ const typeof( ((typeof(*(n)) *)0)->list ) *__mptr = ((n)->list.next); (typeof(*(n)) *)( (char *)__mptr - ((size_t) &((typeof(*(n)) *)0)->list) ); })) // NOWARN + { + ldv_list_del(&msg->list); + ldv_msg_free(msg); + } +} \ No newline at end of file diff --git a/tests/regression/55-loop-unrolling/13-unrolled-loop-no-overflows.c b/tests/regression/55-loop-unrolling/13-unrolled-loop-no-overflows.c new file mode 100644 index 0000000000..84700ac9da --- /dev/null +++ b/tests/regression/55-loop-unrolling/13-unrolled-loop-no-overflows.c @@ -0,0 +1,72 @@ +// PARAM: --enable ana.int.interval_set +// extracted from SV-COMP task ldv-memsafety/memleaks_test12-2.i with --set exp.unrolling-factor 1 + +typedef unsigned int size_t; +struct ldv_list_head { + struct ldv_list_head *next, *prev; +}; +struct ldv_list_head ldv_global_msg_list = {&(ldv_global_msg_list), &(ldv_global_msg_list)}; +struct ldv_msg { + void *data; + struct ldv_list_head list; +}; + +__inline static void __ldv_list_del(struct ldv_list_head *prev, struct ldv_list_head *next) { + next->prev = prev; + prev->next = next; + return; +} + +__inline static void ldv_list_del(struct ldv_list_head *entry) { + __ldv_list_del(entry->prev, entry->next); + return; +} + +void ldv_msg_free(struct ldv_msg *msg) { + free(msg->data); + free((void *)msg); + return; +} + +// ldv_destroy_msgs +void main(void) { + struct ldv_msg *msg; + struct ldv_msg *n; + struct ldv_list_head const *__mptr; + struct ldv_list_head const *__mptr___0; + struct ldv_list_head const *__mptr___1; + + __mptr = (struct ldv_list_head const *)ldv_global_msg_list.next; + msg = (struct ldv_msg *)((char *)__mptr - (size_t)(&((struct ldv_msg *)0)->list)); + __mptr___0 = (struct ldv_list_head const *)msg->list.next; + n = (struct ldv_msg *)((char *)__mptr___0 - (size_t)(&((struct ldv_msg *)0)->list)); + + if (!((unsigned long)(&msg->list) != (unsigned long)(&ldv_global_msg_list))) { // NOWARN + goto loop_end; + } + + ldv_list_del(&msg->list); + ldv_msg_free(msg); + msg = n; + __mptr___1 = (struct ldv_list_head const *)n->list.next; + n = (struct ldv_msg *)((char *)__mptr___1 - (size_t)(&((struct ldv_msg *)0)->list)); + + loop_continue_0: /* CIL Label */; + { + while (1) { + while_continue: /* CIL Label */; + if (!((unsigned long)(&msg->list) != (unsigned long)(&ldv_global_msg_list))) { + goto while_break; + } + + ldv_list_del(&msg->list); + ldv_msg_free(msg); + msg = n; + __mptr___1 = (struct ldv_list_head const *)n->list.next; + n = (struct ldv_msg *)((char *)__mptr___1 - (size_t)(&((struct ldv_msg *)0)->list)); + } + while_break: /* CIL Label */; + } + loop_end: /* CIL Label */; + return; +} \ No newline at end of file diff --git a/tests/regression/56-witness/46-top-bool-invariant.c b/tests/regression/56-witness/46-top-bool-invariant.c index 2c90a55a2d..a11fea0991 100644 --- a/tests/regression/56-witness/46-top-bool-invariant.c +++ b/tests/regression/56-witness/46-top-bool-invariant.c @@ -1,5 +1,5 @@ // PARAM: --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --enable ana.int.def_exc --enable ana.int.interval --enable ana.int.enums --enable ana.int.congruence --enable ana.int.interval_set --disable witness.invariant.inexact-type-bounds - +// CRAM int main() { _Bool x; return 0; diff --git a/tests/regression/56-witness/46-top-bool-invariant.t b/tests/regression/56-witness/46-top-bool-invariant.t index b04d33dda8..741b00966f 100644 --- a/tests/regression/56-witness/46-top-bool-invariant.t +++ b/tests/regression/56-witness/46-top-bool-invariant.t @@ -144,7 +144,7 @@ all: dead: 0 total lines: 2 [Info][Witness] witness generation summary: - total generation entries: 3 + total generation entries: 1 $ yamlWitnessStrip < witness.yml - entry_type: location_invariant @@ -158,28 +158,6 @@ all: string: x == (_Bool)0 || x == (_Bool)1 type: assertion format: C - - entry_type: location_invariant - location: - file_name: 46-top-bool-invariant.c - file_hash: $FILE_HASH - line: 5 - column: 3 - function: main - location_invariant: - string: x <= (_Bool)1 - type: assertion - format: C - - entry_type: location_invariant - location: - file_name: 46-top-bool-invariant.c - file_hash: $FILE_HASH - line: 5 - column: 3 - function: main - location_invariant: - string: (_Bool)0 <= x - type: assertion - format: C all without inexact-type-bounds: diff --git a/tests/regression/56-witness/47-top-int-invariant.c b/tests/regression/56-witness/47-top-int-invariant.c index 45f6106ce8..ef39ecd9b2 100644 --- a/tests/regression/56-witness/47-top-int-invariant.c +++ b/tests/regression/56-witness/47-top-int-invariant.c @@ -1,5 +1,5 @@ // PARAM: --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --enable ana.int.def_exc --enable ana.int.interval --enable ana.int.enums --enable ana.int.congruence --enable ana.int.interval_set --disable witness.invariant.inexact-type-bounds - +// CRAM int main() { int x; return 0; diff --git a/tests/regression/56-witness/50-witness-lifter-fp1.c b/tests/regression/56-witness/50-witness-lifter-fp1.c index 8c6e45e648..1db839cd7b 100644 --- a/tests/regression/56-witness/50-witness-lifter-fp1.c +++ b/tests/regression/56-witness/50-witness-lifter-fp1.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --set ana.specification 'CHECK( init(main()), LTL(G ! call(reach_error())) )' --enable ana.int.interval -// previously fixpoint not reached +// FIXPOINT: previously fixpoint not reached // extracted from sv-benchmarks loops-crafted-1/loopv2 int SIZE = 50000001; int __VERIFIER_nondet_int(); diff --git a/tests/regression/56-witness/52-witness-lifter-ps2.c b/tests/regression/56-witness/52-witness-lifter-ps2.c index bcb7c1410c..1eb3495235 100644 --- a/tests/regression/56-witness/52-witness-lifter-ps2.c +++ b/tests/regression/56-witness/52-witness-lifter-ps2.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --enable witness.graphml.enabled --set ana.specification 'CHECK( init(main()), LTL(G valid-memtrack) )' --set ana.activated[+] memLeak --set ana.path_sens[+] memLeak --set ana.malloc.unique_address_count 1 +// NOCRASH struct _twoIntsStruct { int intOne ; int intTwo ; diff --git a/tests/regression/56-witness/53-witness-lifter-ps3.c b/tests/regression/56-witness/53-witness-lifter-ps3.c index 06b73b3888..ff368a88fc 100644 --- a/tests/regression/56-witness/53-witness-lifter-ps3.c +++ b/tests/regression/56-witness/53-witness-lifter-ps3.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.sv-comp.enabled --enable ana.sv-comp.functions --enable witness.graphml.enabled --set ana.specification 'CHECK( init(main()), LTL(G valid-memtrack) )' --set ana.activated[+] memLeak --set ana.path_sens[+] memLeak --set ana.malloc.unique_address_count 1 +// NOCRASH struct _twoIntsStruct { int intOne ; int intTwo ; diff --git a/tests/regression/58-base-mm-tid/01-tid-toy1.c b/tests/regression/58-base-mm-tid/01-tid-toy1.c index bc3cd18efa..49e96881d1 100644 --- a/tests/regression/58-base-mm-tid/01-tid-toy1.c +++ b/tests/regression/58-base-mm-tid/01-tid-toy1.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.path_sens[+] threadflag --sets ana.base.privatization mutex-meet-tid +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid // Inspired by 36/71 #include #include diff --git a/tests/regression/58-base-mm-tid/15-branched-thread-creation.c b/tests/regression/58-base-mm-tid/15-branched-thread-creation.c index 3292182adc..c7bb43519f 100644 --- a/tests/regression/58-base-mm-tid/15-branched-thread-creation.c +++ b/tests/regression/58-base-mm-tid/15-branched-thread-creation.c @@ -41,7 +41,7 @@ int main(void) { if(!mt) { pthread_mutex_lock(&mutex); - __goblint_check(g==h); //MAYFAIL + __goblint_check(g==h); //FAIL pthread_mutex_unlock(&mutex); } diff --git a/tests/regression/58-base-mm-tid/22-other-assume.c b/tests/regression/58-base-mm-tid/22-other-assume.c index 4f5c73b038..301195ac1f 100644 --- a/tests/regression/58-base-mm-tid/22-other-assume.c +++ b/tests/regression/58-base-mm-tid/22-other-assume.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.path_sens[+] threadflag --sets ana.base.privatization mutex-meet-tid +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid // Copy of 46/03 for base #include #include diff --git a/tests/regression/58-base-mm-tid/23-other-assume-inprec.c b/tests/regression/58-base-mm-tid/23-other-assume-inprec.c index 37e59d3edf..a4bac04c59 100644 --- a/tests/regression/58-base-mm-tid/23-other-assume-inprec.c +++ b/tests/regression/58-base-mm-tid/23-other-assume-inprec.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.path_sens[+] threadflag --sets ana.base.privatization mutex-meet-tid --set ana.activated[+] threadJoins +// PARAM: --set ana.path_sens[+] threadflag --set ana.base.privatization mutex-meet-tid --set ana.activated[+] threadJoins // Copy of 46/04 for base #include #include diff --git a/tests/regression/61-evalAssert/01-union_evalAssert.c b/tests/regression/61-evalAssert/01-union_evalAssert.c index 22e72e0e51..150d1fada2 100644 --- a/tests/regression/61-evalAssert/01-union_evalAssert.c +++ b/tests/regression/61-evalAssert/01-union_evalAssert.c @@ -1,5 +1,5 @@ // PARAM: --set trans.activated[+] "assert" - +// NOCHECK // Running the assert transformation on this test used to yield code that cannot be compiled with gcc, due to superfluous offsets on a pointer struct s { int a; diff --git a/tests/regression/63-affeq/14-norm_inv.c b/tests/regression/63-affeq/14-norm_inv.c index d93fa00cd4..df144fc402 100644 --- a/tests/regression/63-affeq/14-norm_inv.c +++ b/tests/regression/63-affeq/14-norm_inv.c @@ -3,6 +3,7 @@ // Normalization should be triggered when an invertible expression is assigned. // No asserts, likely fixpoint regression. // TODO: used to have list-based matrices, issue was only with those? +// NOCHECK int main() { int A, B; int r, d, p, q; diff --git a/tests/regression/63-affeq/17-verify.c b/tests/regression/63-affeq/17-verify.c index 7ac1b202e4..2ae7d338ba 100644 --- a/tests/regression/63-affeq/17-verify.c +++ b/tests/regression/63-affeq/17-verify.c @@ -1,5 +1,5 @@ //SKIP PARAM: --set ana.activated[+] affeq --sem.int.signed_overflow "assume_none" --enable ana.int.interval -// Error in leq check led to verify error +// FIXPOINT: Error in leq check led to verify error int main() { int n, a, b; diff --git a/tests/regression/66-interval-set-one/00-was_problematic_2.c b/tests/regression/66-interval-set-one/00-was_problematic_2.c index e5b3938a76..187e6f39df 100644 --- a/tests/regression/66-interval-set-one/00-was_problematic_2.c +++ b/tests/regression/66-interval-set-one/00-was_problematic_2.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +// NOCHECK int main(void) { int arr[260]; diff --git a/tests/regression/66-interval-set-one/03-was_problematic_3.c b/tests/regression/66-interval-set-one/03-was_problematic_3.c index 59ce6e5cb6..2a8bd4bc46 100644 --- a/tests/regression/66-interval-set-one/03-was_problematic_3.c +++ b/tests/regression/66-interval-set-one/03-was_problematic_3.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned +// NOCHECK struct some_struct { int dir[7]; diff --git a/tests/regression/66-interval-set-one/14-no-int-context.c b/tests/regression/66-interval-set-one/14-no-int-context.c index 69d2aa2aa7..d701caf59e 100644 --- a/tests/regression/66-interval-set-one/14-no-int-context.c +++ b/tests/regression/66-interval-set-one/14-no-int-context.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.interval_set --set solver slr3t --disable ana.base.context.int - +// NOCHECK int f (int i) { // -2 return i+1; } // -3 void g(int j) { // -4 diff --git a/tests/regression/66-interval-set-one/39-calls.c b/tests/regression/66-interval-set-one/39-calls.c index 67ff46ad77..acfade3084 100644 --- a/tests/regression/66-interval-set-one/39-calls.c +++ b/tests/regression/66-interval-set-one/39-calls.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval_set --disable ana.int.def_exc --set ana.base.arrays.domain partitioned // Variable-sized arrays +// NOCHECK void foo(int n, int a[n]); void foo2(int n, int a[30][n]); void foo3(int n, int a[n][30]); diff --git a/tests/regression/66-interval-set-one/51-widen-sides.c b/tests/regression/66-interval-set-one/51-widen-sides.c index b086baf026..53fe749704 100644 --- a/tests/regression/66-interval-set-one/51-widen-sides.c +++ b/tests/regression/66-interval-set-one/51-widen-sides.c @@ -1,4 +1,4 @@ -// PARAM: --set ana.ctx_insens "['base', 'mallocWrapper']" --enable ana.int.interval_set --sets solvers.td3.side_widen sides-local +// PARAM: --set ana.ctx_insens "['base', 'mallocWrapper']" --enable ana.int.interval_set --set solvers.td3.side_widen sides-local #include int further(int n) { diff --git a/tests/regression/66-interval-set-one/93-enum.c b/tests/regression/66-interval-set-one/93-enum.c index 5d2b45043d..ca769fb126 100644 --- a/tests/regression/66-interval-set-one/93-enum.c +++ b/tests/regression/66-interval-set-one/93-enum.c @@ -1,4 +1,5 @@ // PARAM: --disable ana.int.interval_set --disable ana.int.def_exc --enable ana.int.enums +// NOCHECK void main(){ int n = 1; for (; n; n++) { // fixed point not reached here diff --git a/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c b/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c index 8d56fa0e3c..f5291fdcdc 100644 --- a/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c +++ b/tests/regression/67-interval-sets-two/03-def_exc-interval-inconsistent.c @@ -1,6 +1,7 @@ // PARAM: --enable ana.int.def_exc --enable ana.int.interval_set --enable ana.sv-comp.functions --set sem.int.signed_overflow assume_none --set ana.int.refinement never // used to crash in branch when is_bool returned true, but to_bool returned None on (0,[1,1]) // manually minimized from sv-benchmarks/c/recursive/MultCommutative-2.c +// NOCHECK extern int __VERIFIER_nondet_int(void); void f(int m) { diff --git a/tests/regression/67-interval-sets-two/04-unsupported.c b/tests/regression/67-interval-sets-two/04-unsupported.c index 97ce11258a..4127da4d8b 100644 --- a/tests/regression/67-interval-sets-two/04-unsupported.c +++ b/tests/regression/67-interval-sets-two/04-unsupported.c @@ -1,6 +1,7 @@ // PARAM: --enable ana.int.interval_set --disable exp.fast_global_inits --set ana.base.arrays.domain partitioned // This is just to test that the analysis does not cause problems for features that are not explicitly dealt with +// NOCHECK: what problems? int main(void) { callok(); } diff --git a/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c b/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c index af46023135..a5e5e937cb 100644 --- a/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c +++ b/tests/regression/67-interval-sets-two/14-trylock_rc_slr.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set --set solver slr3t +// NOCHECK #include pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/tests/regression/67-interval-sets-two/15-interval-bot.c b/tests/regression/67-interval-sets-two/15-interval-bot.c index ab7d043b92..6ab90062ae 100644 --- a/tests/regression/67-interval-sets-two/15-interval-bot.c +++ b/tests/regression/67-interval-sets-two/15-interval-bot.c @@ -1,5 +1,5 @@ // PARAM: --enable ana.int.interval_set --enable ana.int.def_exc - +// NOCHECK int main(){ unsigned long long a ; diff --git a/tests/regression/67-interval-sets-two/16-branched-thread-creation.c b/tests/regression/67-interval-sets-two/16-branched-thread-creation.c index c309ec8ffd..d97f800621 100644 --- a/tests/regression/67-interval-sets-two/16-branched-thread-creation.c +++ b/tests/regression/67-interval-sets-two/16-branched-thread-creation.c @@ -41,7 +41,7 @@ int main(void) { if(!mt) { pthread_mutex_lock(&mutex); - __goblint_check(g==h); //MAYFAIL + __goblint_check(g==h); //FAIL pthread_mutex_unlock(&mutex); } diff --git a/tests/regression/67-interval-sets-two/24-arithmetic-bot.c b/tests/regression/67-interval-sets-two/24-arithmetic-bot.c index f238788bb3..f9a2d0086a 100644 --- a/tests/regression/67-interval-sets-two/24-arithmetic-bot.c +++ b/tests/regression/67-interval-sets-two/24-arithmetic-bot.c @@ -1,5 +1,6 @@ // PARAM: --enable ana.int.interval_set --enable ana.int.def_exc // from: ldv-linux-3.0/usb_urb-drivers-vhost-vhost_net.ko.cil.out.i +// NOCHECK typedef unsigned long long u64; int main( ) diff --git a/tests/regression/67-interval-sets-two/31-ptrdiff.c b/tests/regression/67-interval-sets-two/31-ptrdiff.c index d7bd4667df..d6135f8734 100644 --- a/tests/regression/67-interval-sets-two/31-ptrdiff.c +++ b/tests/regression/67-interval-sets-two/31-ptrdiff.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set --set ana.base.arrays.domain partitioned --set ana.activated[+] var_eq +// NOCHECK int *tmp; int main () diff --git a/tests/regression/67-interval-sets-two/44-comparision-bot.c b/tests/regression/67-interval-sets-two/44-comparision-bot.c index 5b3622828a..eb6e90b5ea 100644 --- a/tests/regression/67-interval-sets-two/44-comparision-bot.c +++ b/tests/regression/67-interval-sets-two/44-comparision-bot.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set --enable ana.int.def_exc +// NOCHECK #include int main(){ int a = 0; diff --git a/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c b/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c index e1345155d9..df1d7346bd 100644 --- a/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c +++ b/tests/regression/67-interval-sets-two/56-interval-set-dead-code.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set +// NOCHECK #include #include diff --git a/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c b/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c index b63ebd6fab..1a709243cb 100644 --- a/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c +++ b/tests/regression/67-interval-sets-two/58-interval-set-dead-code-with-fun-call.c @@ -1,4 +1,5 @@ // PARAM: --enable ana.int.interval_set +// NOCHECK #include #include diff --git a/tests/regression/71-doublelocking/15-rec-dyn-nested.c b/tests/regression/71-doublelocking/15-rec-dyn-nested.c index d5dac9cd81..472783a9f3 100644 --- a/tests/regression/71-doublelocking/15-rec-dyn-nested.c +++ b/tests/regression/71-doublelocking/15-rec-dyn-nested.c @@ -1,5 +1,5 @@ // PARAM: --set ana.activated[+] 'pthreadMutexType' -// Check we don't have a stack overflow because of tracking multiplicities +// NOCRASH: Check we don't have a stack overflow because of tracking multiplicities #define _GNU_SOURCE #include #include diff --git a/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c b/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c index cc9819950f..93d4d6f307 100644 --- a/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c +++ b/tests/regression/74-invalid_deref/15-juliet-uaf-global-var.c @@ -1,4 +1,5 @@ //PARAM: --set ana.activated[+] useAfterFree +// NOCHECK #include int *global; diff --git a/tests/regression/80-context_gas/23-per-fun.c b/tests/regression/80-context_gas/23-per-fun.c new file mode 100644 index 0000000000..1f64317b0f --- /dev/null +++ b/tests/regression/80-context_gas/23-per-fun.c @@ -0,0 +1,29 @@ +// PARAM: --enable ana.int.interval_set --set ana.context.gas_value 3 --set ana.context.gas_scope function +int nr(int x, int y) { + // Non-recursive, but would fail with global scope as gas for f is exhausted + __goblint_check(x==y); +} + +int f(int x, int y) +{ + int top; + if (x == 0) + { + return y; + } + + if(top) { + nr(5,5); + } else { + nr(6,6); + } + + return f(x - 1, y - 1); +} + +int main() +{ + // main -> f(3,3) -> f(2,2) -> f(1,1) -> f(0,0) -> return 0 + // 4 recursive calls -> boundary (excluded) + __goblint_check(f(3, 3) == 0); // UNKNOWN +} diff --git a/tests/regression/80-context_gas/24-per-fun-ex.c b/tests/regression/80-context_gas/24-per-fun-ex.c new file mode 100644 index 0000000000..ab5d203c45 --- /dev/null +++ b/tests/regression/80-context_gas/24-per-fun-ex.c @@ -0,0 +1,44 @@ +// PARAM: --enable ana.int.interval_set --set ana.context.gas_value 3 --set ana.context.gas_scope function +int nr(int x, int y) { + // Non-recursive, but would fail with global scope as gas for f is exhausted + __goblint_check(x==y); +} + +// Checks that gas is also applied to further functions +int g(int x, int y) +{ + int top; + if (x < -100000) + { + return y; + } + + if(top) { + nr(5,5); + } else { + nr(6,6); + } + + return g(x - 1, y - 1); +} + +int f(int x, int y) +{ + int top; + + if (x == 0) + { + return y; + } + + g(x,y); + + return f(x - 1, y - 1); +} + +int main() +{ + // main -> f(3,3) -> f(2,2) -> f(1,1) -> f(0,0) -> return 0 + // 4 recursive calls -> boundary (excluded) + __goblint_check(f(3, 3) == 0); // UNKNOWN +} diff --git a/tests/regression/80-context_gas/25-per-fun-nonterm.c b/tests/regression/80-context_gas/25-per-fun-nonterm.c new file mode 100644 index 0000000000..4c3871688f --- /dev/null +++ b/tests/regression/80-context_gas/25-per-fun-nonterm.c @@ -0,0 +1,29 @@ +//PARAM: --enable ana.int.interval_set --set ana.context.gas_value 3 --set ana.context.gas_scope function +// NOTIMEOUT +void h(int n) { + int x; + + if(x) { + return; + } + + g(n+1); + h(n+1); +} + +void g(int n) { + int x; + + if(x) { + return; + } + + g(n+1); + h(n+1); +} + +int main() +{ + g(0); + h(0); +} diff --git a/tests/regression/cfg/foo.t/run.t b/tests/regression/cfg/foo.t/run.t index 02f6c1e5e0..cd890b7a19 100644 --- a/tests/regression/cfg/foo.t/run.t +++ b/tests/regression/cfg/foo.t/run.t @@ -67,7 +67,7 @@ total lines: 6 [Warning][Deadcode][CWE-571] condition 'a > 0' (possibly inserted by CIL) is always true (foo.c:3:10-3:20) [Info][Witness] witness generation summary: - total generation entries: 15 + total generation entries: 10 $ yamlWitnessStrip < witness.yml - entry_type: loop_invariant @@ -103,17 +103,6 @@ string: b == 0 type: assertion format: C - - entry_type: location_invariant - location: - file_name: foo.c - file_hash: $FILE_HASH - line: 7 - column: 3 - function: main - location_invariant: - string: a != 0 - type: assertion - format: C - entry_type: location_invariant location: file_name: foo.c @@ -125,17 +114,6 @@ string: 1 <= a type: assertion format: C - - entry_type: location_invariant - location: - file_name: foo.c - file_hash: $FILE_HASH - line: 7 - column: 3 - function: main - location_invariant: - string: 0 <= a - type: assertion - format: C - entry_type: location_invariant location: file_name: foo.c @@ -158,17 +136,6 @@ string: b != 0 type: assertion format: C - - entry_type: location_invariant - location: - file_name: foo.c - file_hash: $FILE_HASH - line: 5 - column: 5 - function: main - location_invariant: - string: a != 1 - type: assertion - format: C - entry_type: location_invariant location: file_name: foo.c @@ -202,17 +169,6 @@ string: b != 0 type: assertion format: C - - entry_type: location_invariant - location: - file_name: foo.c - file_hash: $FILE_HASH - line: 4 - column: 5 - function: main - location_invariant: - string: a != 0 - type: assertion - format: C - entry_type: location_invariant location: file_name: foo.c @@ -224,14 +180,3 @@ string: 1 <= a type: assertion format: C - - entry_type: location_invariant - location: - file_name: foo.c - file_hash: $FILE_HASH - line: 4 - column: 5 - function: main - location_invariant: - string: 0 <= a - type: assertion - format: C diff --git a/tests/regression/dune b/tests/regression/dune index 8f550d190e..0fd8052478 100644 --- a/tests/regression/dune +++ b/tests/regression/dune @@ -2,7 +2,8 @@ (_ (binaries ./cfg/util/cfgDot.exe - ../util/yamlWitnessStrip.exe))) + ../util/yamlWitnessStrip.exe + ../util/yamlWitnessStripDiff.exe))) (rule (alias runtest) @@ -24,4 +25,5 @@ (alias runcramtest) (deps (package goblint) - %{bin:yamlWitnessStrip})) + %{bin:yamlWitnessStrip} + %{bin:yamlWitnessStripDiff})) diff --git a/tests/regression/issue-94.t/issue-94.c b/tests/regression/issue-94.t/issue-94.c new file mode 100644 index 0000000000..7bdc65b0a8 --- /dev/null +++ b/tests/regression/issue-94.t/issue-94.c @@ -0,0 +1,14 @@ +#include + +int main() { + int x; + if (1) + x = 1; + else + x = 2; + if (x) + x = 1; + else + x = 2; + assert(x > 1 && x < 0); +} diff --git a/tests/regression/issue-94.t/run.t b/tests/regression/issue-94.t/run.t new file mode 100644 index 0000000000..40b73f247b --- /dev/null +++ b/tests/regression/issue-94.t/run.t @@ -0,0 +1,14 @@ + $ goblint --enable ana.dead-code.lines --enable ana.dead-code.branches issue-94.c + [Error][Assert] Assertion "tmp" will fail. (issue-94.c:13:3-13:35) + [Warning][Deadcode] Function 'main' does not return + [Warning][Deadcode] Function 'main' has dead code: + on line 8 (issue-94.c:8-8) + on line 12 (issue-94.c:12-12) + on line 14 (issue-94.c:14-14) + [Warning][Deadcode] Logical lines of code (LLoC) summary: + live: 6 + dead: 3 + total lines: 9 + [Warning][Deadcode][CWE-571] condition '1' is always true (issue-94.c:5:7-5:8) + [Warning][Deadcode][CWE-571] condition 'x' is always true (issue-94.c:9:7-9:8) + [Warning][Deadcode][CWE-570] condition 'x > 1' is always false (issue-94.c:13:3-13:35) diff --git a/tests/regression/witness/int.t/int.c b/tests/regression/witness/int.t/int.c new file mode 100644 index 0000000000..4ad15b09ad --- /dev/null +++ b/tests/regression/witness/int.t/int.c @@ -0,0 +1,17 @@ +#include +extern int __VERIFIER_nondet_int(); + +int main() { + int i; + i = __VERIFIER_nondet_int(); + + if (i < 100) + __goblint_check(1); + + if (50 < i && i < 100) + __goblint_check(1); + + if (i == 42 || i == 5 || i == 101) + __goblint_check(1); + return 0; +} diff --git a/tests/regression/witness/int.t/run.t b/tests/regression/witness/int.t/run.t new file mode 100644 index 0000000000..6b4784ce32 --- /dev/null +++ b/tests/regression/witness/int.t/run.t @@ -0,0 +1,45 @@ + $ goblint --enable ana.sv-comp.functions --enable witness.yaml.enabled --set witness.yaml.entry-types '["location_invariant"]' --enable ana.int.def_exc --enable ana.int.enums --enable ana.int.interval --enable ana.int.congruence --disable ana.int.interval_set --disable witness.invariant.split-conjunction int.c + [Success][Assert] Assertion "1" will succeed (int.c:9:5-9:23) + [Success][Assert] Assertion "1" will succeed (int.c:12:5-12:23) + [Success][Assert] Assertion "1" will succeed (int.c:15:5-15:23) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 10 + dead: 0 + total lines: 10 + [Info][Witness] witness generation summary: + total generation entries: 3 + + $ yamlWitnessStrip < witness.yml + - entry_type: location_invariant + location: + file_name: int.c + file_hash: $FILE_HASH + line: 15 + column: 5 + function: main + location_invariant: + string: (i == 5 || i == 42) || i == 101 + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: int.c + file_hash: $FILE_HASH + line: 12 + column: 5 + function: main + location_invariant: + string: 51 <= i && i <= 99 + type: assertion + format: C + - entry_type: location_invariant + location: + file_name: int.c + file_hash: $FILE_HASH + line: 9 + column: 5 + function: main + location_invariant: + string: i <= 99 + type: assertion + format: C diff --git a/tests/regression/witness/violation.t/correct-hard.c b/tests/regression/witness/violation.t/correct-hard.c new file mode 100644 index 0000000000..ce2b50a0c0 --- /dev/null +++ b/tests/regression/witness/violation.t/correct-hard.c @@ -0,0 +1,9 @@ +void reach_error(){} +extern int __VERIFIER_nondet_int(); + +int main() { + int x = __VERIFIER_nondet_int(); + if (x != x) + reach_error(); + return 0; +} diff --git a/tests/regression/witness/violation.t/correct-hard.yml b/tests/regression/witness/violation.t/correct-hard.yml new file mode 100644 index 0000000000..b8af2c2193 --- /dev/null +++ b/tests/regression/witness/violation.t/correct-hard.yml @@ -0,0 +1,26 @@ +- entry_type: violation_sequence + metadata: + format_version: "2.0" + uuid: 4412af70-389a-475e-849c-e57e5b92019e + creation_time: 2024-06-14T15:35:00+03:00 + producer: + name: Simmo Saan + version: n/a + task: + input_files: + - correct-hard.c + input_file_hashes: + correct-hard.c: 5cc49c1ce5a9aef64286c2a6e57f6955c5f4f9b19b43056507ae87a802802447 + specification: G ! call(reach_error()) + data_model: ILP32 + language: C + content: + - segment: + - waypoint: + type: target + action: follow + location: + file_name: correct-hard.c + line: 7 + column: 5 + function: main diff --git a/tests/regression/witness/violation.t/correct.c b/tests/regression/witness/violation.t/correct.c new file mode 100644 index 0000000000..30f58a2f7e --- /dev/null +++ b/tests/regression/witness/violation.t/correct.c @@ -0,0 +1,5 @@ +void reach_error(){} + +int main() { + return 0; +} diff --git a/tests/regression/witness/violation.t/correct.yml b/tests/regression/witness/violation.t/correct.yml new file mode 100644 index 0000000000..1f1d4f41da --- /dev/null +++ b/tests/regression/witness/violation.t/correct.yml @@ -0,0 +1,26 @@ +- entry_type: violation_sequence + metadata: + format_version: "2.0" + uuid: 4412af70-389a-475e-849c-e57e5b92019d + creation_time: 2024-06-14T15:35:00+03:00 + producer: + name: Simmo Saan + version: n/a + task: + input_files: + - correct.c + input_file_hashes: + correct.c: 6f760cf7f33fc152738bf3514fe623cc94e52cad9ddc2f0e744595ce0de07530 + specification: G ! call(reach_error()) + data_model: ILP32 + language: C + content: + - segment: + - waypoint: + type: target + action: follow + location: + file_name: correct.c + line: 4 + column: 3 + function: main diff --git a/tests/regression/witness/violation.t/incorrect.c b/tests/regression/witness/violation.t/incorrect.c new file mode 100644 index 0000000000..ff56fa2ef4 --- /dev/null +++ b/tests/regression/witness/violation.t/incorrect.c @@ -0,0 +1,6 @@ +void reach_error(){} + +int main() { + reach_error(); + return 0; +} diff --git a/tests/regression/witness/violation.t/incorrect.yml b/tests/regression/witness/violation.t/incorrect.yml new file mode 100644 index 0000000000..dd57ce3ca1 --- /dev/null +++ b/tests/regression/witness/violation.t/incorrect.yml @@ -0,0 +1,26 @@ +- entry_type: violation_sequence + metadata: + format_version: "2.0" + uuid: 4412af70-389a-475e-849c-e57e5b92019c + creation_time: 2024-06-14T15:35:00+03:00 + producer: + name: Simmo Saan + version: n/a + task: + input_files: + - incorrect.c + input_file_hashes: + incorrect.c: 1af4fd9e76418e4b95af9950b58248127e7c2d9eb791e1c9b92da53952e0fca2 + specification: G ! call(reach_error()) + data_model: ILP32 + language: C + content: + - segment: + - waypoint: + type: target + action: follow + location: + file_name: incorrect.c + line: 4 + column: 3 + function: main diff --git a/tests/regression/witness/violation.t/run.t b/tests/regression/witness/violation.t/run.t new file mode 100644 index 0000000000..1fc408635e --- /dev/null +++ b/tests/regression/witness/violation.t/run.t @@ -0,0 +1,57 @@ +Violation witness for a correct program can be refuted by proving the program correct and returning `true`: + + $ goblint --enable ana.sv-comp.enabled --set witness.yaml.entry-types[+] violation_sequence --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" correct.c --set witness.yaml.validate correct.yml + [Info] SV-COMP specification: CHECK( init(main()), LTL(G ! call(reach_error())) ) + [Warning][Deadcode] Function 'reach_error' is uncalled: 1 LLoC (correct.c:1:1-1:20) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 2 + dead: 1 (1 in uncalled functions) + total lines: 3 + [Info][Witness] witness validation summary: + confirmed: 0 + unconfirmed: 0 + refuted: 0 + error: 0 + unchecked: 0 + unsupported: 0 + disabled: 0 + total validation entries: 0 + SV-COMP result: true + +If a correct progtam cannot be proven correct, return `unknown` for the violation witness: + + $ goblint --set ana.activated[-] expRelation --enable ana.sv-comp.functions --enable ana.sv-comp.enabled --set witness.yaml.entry-types[+] violation_sequence --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" correct-hard.c --set witness.yaml.validate correct-hard.yml + [Info] SV-COMP specification: CHECK( init(main()), LTL(G ! call(reach_error())) ) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 7 + dead: 0 + total lines: 7 + [Info][Witness] witness validation summary: + confirmed: 0 + unconfirmed: 0 + refuted: 0 + error: 0 + unchecked: 0 + unsupported: 0 + disabled: 0 + total validation entries: 0 + SV-COMP result: unknown + +Violation witness for an incorrect program cannot be proven correct, so return `unknown`: + + $ goblint --enable ana.sv-comp.enabled --set witness.yaml.entry-types[+] violation_sequence --set ana.specification "CHECK( init(main()), LTL(G ! call(reach_error())) )" incorrect.c --set witness.yaml.validate incorrect.yml + [Info] SV-COMP specification: CHECK( init(main()), LTL(G ! call(reach_error())) ) + [Info][Deadcode] Logical lines of code (LLoC) summary: + live: 4 + dead: 0 + total lines: 4 + [Info][Witness] witness validation summary: + confirmed: 0 + unconfirmed: 0 + refuted: 0 + error: 0 + unchecked: 0 + unsupported: 0 + disabled: 0 + total validation entries: 0 + SV-COMP result: unknown diff --git a/tests/unit/maindomaintest.ml b/tests/unit/maindomaintest.ml index 8e1db76b83..4b379a252f 100644 --- a/tests/unit/maindomaintest.ml +++ b/tests/unit/maindomaintest.ml @@ -28,9 +28,6 @@ let domains: (module Lattice.S) list = [ (* (module IntDomainProperties.IntegerSet); (* TODO: top properties error *) *) (module IntDomain.Lifted); (* not abstraction of IntegerSet *) - (* TODO: move to intDomains if passing *) - (module IntDomain.Booleans); - (* TODO: fix *) (* (module IntDomain.Enums); *) (* (module IntDomain.IntDomTuple); *) diff --git a/tests/util/dune b/tests/util/dune index 6637217651..0e32304d4f 100644 --- a/tests/util/dune +++ b/tests/util/dune @@ -1,5 +1,5 @@ -(executable - (name yamlWitnessStrip) +(executables + (names yamlWitnessStrip yamlWitnessStripDiff) (libraries batteries.unthreaded goblint_std diff --git a/tests/util/yamlWitnessStrip.ml b/tests/util/yamlWitnessStrip.ml index 8a5046d6ff..ece604f9a8 100644 --- a/tests/util/yamlWitnessStrip.ml +++ b/tests/util/yamlWitnessStrip.ml @@ -1,55 +1,5 @@ open Goblint_lib -open YamlWitnessType - -module StrippedEntry = -struct - type t = { - entry_type: EntryType.t; - } - [@@deriving ord] - - let strip_file_hashes {entry_type} = - let stripped_file_hash = "$FILE_HASH" in - let location_strip_file_hash location: Location.t = - {location with file_hash = stripped_file_hash} - in - let target_strip_file_hash target: Target.t = - {target with file_hash = stripped_file_hash} - in - let invariant_strip_file_hash ({invariant_type}: InvariantSet.Invariant.t): InvariantSet.Invariant.t = - let invariant_type: InvariantSet.InvariantType.t = - match invariant_type with - | LocationInvariant x -> - LocationInvariant {x with location = location_strip_file_hash x.location} - | LoopInvariant x -> - LoopInvariant {x with location = location_strip_file_hash x.location} - in - {invariant_type} - in - let entry_type: EntryType.t = - match entry_type with - | LocationInvariant x -> - LocationInvariant {x with location = location_strip_file_hash x.location} - | LoopInvariant x -> - LoopInvariant {x with location = location_strip_file_hash x.location} - | FlowInsensitiveInvariant x -> - FlowInsensitiveInvariant x (* no location to strip *) - | PreconditionLoopInvariant x -> - PreconditionLoopInvariant {x with location = location_strip_file_hash x.location} - | LoopInvariantCertificate x -> - LoopInvariantCertificate {x with target = target_strip_file_hash x.target} - | PreconditionLoopInvariantCertificate x -> - PreconditionLoopInvariantCertificate {x with target = target_strip_file_hash x.target} - | InvariantSet x -> - InvariantSet {content = List.map invariant_strip_file_hash x.content} - in - {entry_type} - - let to_yaml {entry_type} = - `O ([ - ("entry_type", `String (EntryType.entry_type entry_type)); - ] @ EntryType.to_yaml' entry_type) -end +open YamlWitnessStripCommon (* Use set for output, so order is deterministic regardless of Goblint. *) module StrippedEntrySet = Set.Make (StrippedEntry) @@ -69,18 +19,7 @@ let main () = stripped_entries ) StrippedEntrySet.empty yaml_entries in - let stripped_yaml_entries = - StrippedEntrySet.elements stripped_entries - |> List.map StrippedEntry.strip_file_hashes - |> List.rev_map StrippedEntry.to_yaml - in - let stripped_yaml = `A stripped_yaml_entries in - (* to_file/to_string uses a fixed-size buffer... *) - let stripped_yaml_str = match GobYaml.to_string' stripped_yaml with - | Ok text -> text - | Error (`Msg m) -> failwith ("Yaml.to_string: " ^ m) - in - Batteries.output_string Batteries.stdout stripped_yaml_str + print_stripped_entries stripped_entries let () = main () diff --git a/tests/util/yamlWitnessStripCommon.ml b/tests/util/yamlWitnessStripCommon.ml new file mode 100644 index 0000000000..d1656d3ddc --- /dev/null +++ b/tests/util/yamlWitnessStripCommon.ml @@ -0,0 +1,101 @@ +open Goblint_lib +open YamlWitnessType + +module StrippedEntry = +struct + type t = { + entry_type: EntryType.t; + } + [@@deriving ord] + + let strip_file_hashes {entry_type} = + let stripped_file_hash = "$FILE_HASH" in + let location_strip_file_hash (location: Location.t): Location.t = + let file_hash = + match location.file_hash with + | Some _ -> Some stripped_file_hash (* TODO: or just map to None always? *) + | None -> None + in + {location with file_hash} + in + let target_strip_file_hash target: Target.t = + {target with file_hash = stripped_file_hash} + in + let invariant_strip_file_hash ({invariant_type}: InvariantSet.Invariant.t): InvariantSet.Invariant.t = + let invariant_type: InvariantSet.InvariantType.t = + match invariant_type with + | LocationInvariant x -> + LocationInvariant {x with location = location_strip_file_hash x.location} + | LoopInvariant x -> + LoopInvariant {x with location = location_strip_file_hash x.location} + in + {invariant_type} + in + let waypoint_strip_file_hash ({waypoint_type}: ViolationSequence.Waypoint.t): ViolationSequence.Waypoint.t = + let waypoint_type: ViolationSequence.WaypointType.t = + match waypoint_type with + | Assumption x -> + Assumption {x with location = location_strip_file_hash x.location} + | Target x -> + Target {x with location = location_strip_file_hash x.location} + | FunctionEnter x -> + FunctionEnter {x with location = location_strip_file_hash x.location} + | FunctionReturn x -> + FunctionReturn {x with location = location_strip_file_hash x.location} + | Branching x -> + Branching {x with location = location_strip_file_hash x.location} + in + {waypoint_type} + in + let segment_strip_file_hash ({segment}: ViolationSequence.Segment.t): ViolationSequence.Segment.t = + {segment = List.map waypoint_strip_file_hash segment} + in + let entry_type: EntryType.t = + match entry_type with + | LocationInvariant x -> + LocationInvariant {x with location = location_strip_file_hash x.location} + | LoopInvariant x -> + LoopInvariant {x with location = location_strip_file_hash x.location} + | FlowInsensitiveInvariant x -> + FlowInsensitiveInvariant x (* no location to strip *) + | PreconditionLoopInvariant x -> + PreconditionLoopInvariant {x with location = location_strip_file_hash x.location} + | LoopInvariantCertificate x -> + LoopInvariantCertificate {x with target = target_strip_file_hash x.target} + | PreconditionLoopInvariantCertificate x -> + PreconditionLoopInvariantCertificate {x with target = target_strip_file_hash x.target} + | InvariantSet x -> + InvariantSet {content = List.map invariant_strip_file_hash x.content} + | ViolationSequence x -> + ViolationSequence {content = List.map segment_strip_file_hash x.content} + in + {entry_type} + + let to_yaml {entry_type} = + `O ([ + ("entry_type", `String (EntryType.entry_type entry_type)); + ] @ EntryType.to_yaml' entry_type) + + let of_yaml y = + let open GobYaml in + let+ entry_type = y |> EntryType.of_yaml in + {entry_type} +end + +(* Use set for output, so order is deterministic regardless of Goblint. *) +module StrippedEntrySet = Set.Make (StrippedEntry) + +let print_stripped_entries stripped_entries = + let stripped_yaml_entries = + StrippedEntrySet.elements stripped_entries + |> List.map StrippedEntry.strip_file_hashes + |> List.rev_map StrippedEntry.to_yaml + in + + let stripped_yaml = `A stripped_yaml_entries in + (* to_file/to_string uses a fixed-size buffer... *) + let stripped_yaml_str = match GobYaml.to_string' stripped_yaml with + | Ok text -> text + | Error (`Msg m) -> failwith ("Yaml.to_string: " ^ m) + in + Batteries.output_string Batteries.stdout stripped_yaml_str diff --git a/tests/util/yamlWitnessStripDiff.ml b/tests/util/yamlWitnessStripDiff.ml new file mode 100644 index 0000000000..8a993382b3 --- /dev/null +++ b/tests/util/yamlWitnessStripDiff.ml @@ -0,0 +1,29 @@ +open YamlWitnessStripCommon + +let read_stripped_entries path = + let yaml_str = BatFile.with_file_in path BatIO.read_all in + let yaml = Yaml.of_string_exn yaml_str in + let yaml_entries = yaml |> GobYaml.list |> BatResult.get_ok in + + List.fold_left (fun stripped_entries yaml_entry -> + match StrippedEntry.of_yaml yaml_entry with + | Ok stripped_entry -> + StrippedEntrySet.add stripped_entry stripped_entries + | Error (`Msg e) -> + Format.eprintf "couldn't parse entry: %s" e; + stripped_entries + ) StrippedEntrySet.empty yaml_entries + +let main () = + let left_stripped_entries = read_stripped_entries Sys.argv.(1) in + let right_stripped_entries = read_stripped_entries Sys.argv.(2) in + let left_only_stripped_entries = StrippedEntrySet.diff left_stripped_entries right_stripped_entries in + let right_only_stripped_entries = StrippedEntrySet.diff right_stripped_entries left_stripped_entries in + + print_endline "# Left-only entries:"; + print_stripped_entries left_only_stripped_entries; + print_endline "---"; + print_endline "# Right-only entries:"; + print_stripped_entries right_only_stripped_entries + +let () = main ()