├── .travis.yml ├── CONTRIBUTE.md ├── COPYRIGHT ├── Cargo.toml ├── EXAMPLES.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── USAGE.md ├── build.rs ├── documents ├── ProjectPlan.md ├── RequirementsSpecificationDocument_v0.5.pdf ├── RequirementsSpecificationDocument_v1.0.pdf ├── RequirementsSpecificationDocument_v1.1.pdf ├── RequirementsSpecificationDocumentv1.2.pdf ├── RequirementsSpecificationDocumentv1.3.pdf ├── RiskManagementPlan.pdf └── SoftwareArchitectureDesignDocument_v1.0.pdf ├── examples └── README.md ├── scripts └── travis-z3.sh ├── src ├── expression │ └── mod.rs ├── lib.rs ├── parser │ ├── expression_parser.lalrpop │ ├── expression_parser.rs │ └── mod.rs ├── reporting │ └── mod.rs ├── smt_output │ └── mod.rs ├── tests │ ├── mod.rs │ ├── system_tests.rs │ ├── test_expression.rs │ └── test_reporting.rs └── weakest_precondition │ ├── mod.rs │ └── overflow.rs └── tests ├── test_assert.rs ├── test_boolean_arithmetic.rs ├── test_conditionals.rs ├── test_conditions.rs ├── test_fail_invalid.rs ├── test_fail_valid.rs ├── test_signed.rs └── test_unsigned.rs /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | script: cargo test --lib 4 | 5 | rust: 6 | - nightly-2016-08-12 7 | 8 | env: 9 | global: 10 | secure: OinAZ2GfYnzFEB433bDd+G/DYUkDJLsPY/YRZEnkLRH5L9fK3UOe+FzkyZqa8GyCxDfVDV9yRyGfsRs2cIA3uXI0O3ZWGaEMjdhZODlvqdHQP86jdxZDZxjX+KSwOt9cZrB2rhmHImFzaZA+uS3TvFmbEIOvFknrMNU9od8KVqtcLxhHRmqkM7Zwd5oBVIsusGm0MLrXkRM621rCVutQVFxl5rYcD4/mYiPAew7xyWWzxyK+zBxJzgBLgWSe81HZmJKKEKihyWrkOLD8lGxHGFTS2zcASvwWEDGIbjbPvjzNBreG247RzO+TMPyBvRUmIHP+awn9ENJ08HB1hrfKKFdrM7+G89vOivWjs86n8PYSUAfWXuN8zVZ4KOYJr5L/sTKKP0JRcB2gicW2LY4kmQcVGjHrDnZjJg9qxR6wyDriDV5/jR+4Ko4M79HOAfmQnfrUeG2R6EF/TH+7b/+Qd2vofBZlCvCn+59raYsnMN0rjbq70WdCn+CFAHl2gc/UBk2clDjgnHiRAuzQ4kaBHeC/ieTB+jV44Dl7K9YtD0ce/Fkc0jRAnO30wIid73KYM9lZiY4/D3YULAQMXNhDlRLi+DPb2r+Tidem3dbwKGHIl6GKHJ8t2Chu8MFv51RYToevW7sWtn6RQZuBHTzbuTBqUPXaKaBeqU5g6jJjC+I= 11 | 12 | before_install: 13 | - mkdir -p ~/.local/bin 14 | - mkdir -p ~/.local/lib 15 | - export PATH=~/.local/bin:$PATH 16 | - export LD_LIBRARY_PATH=~/.local/lib:$LD_LIBRARY_PATH 17 | - chmod 700 ./scripts/travis-z3.sh 18 | - ./scripts/travis-z3.sh 19 | 20 | after_success: | 21 | [ $TRAVIS_BRANCH = master ] && 22 | [ $TRAVIS_PULL_REQUEST = false ] && 23 | cargo rustdoc -- --no-defaults --passes "collapse-docs" --passes "unindent-comments" --passes "strip-priv-imports" && 24 | echo "" > target/doc/index.html && 25 | sudo pip install ghp-import && 26 | ghp-import -n target/doc && 27 | git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages 28 | -------------------------------------------------------------------------------- /CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | # Contributing to rustproof 2 | 3 | If you're looking to contribute to rustproof, this document should help you understand the design structure of the project. 4 | 5 | Last updated Aug 24 6 | 7 | ## Overview 8 | A brief overview of the rustproof directory. More details on each module are in the next section. 9 | 10 | `src` 11 | : where the rustproof internals live. 12 | 13 | `tests` 14 | : system tests are located here. Testing is covered in more detail in the rustproof internals section. 15 | 16 | `examples` 17 | : examples that a new rustproof user can examine and run using `cargo build --example ` 18 | 19 | `scripts` 20 | : currently the only script here is for Travis CI 21 | 22 | ## rustproof internals 23 | Within `src`, you'll find the following modules: 24 | 25 | ### `src/expression` 26 | This module is what creates rustproof's internal representations of logical expressions. Its functions are used in multiple places to create expressions from pre/post conditions, and from user written code (ultimately, from rust's MIR statements). 27 | 28 | Files: `mod.rs` 29 | 30 | ### `src/parser` 31 | The parser that uses generates `expression`s from a user's pre/post conditions. The `mod.rs` file has a function for checking the `#[condition]` attribute for errors, and a function for calling the parser. 32 | 33 | Some more details on the parser: `expression_parser.rs` is not intended to be modified manually. It is a LR(1) parser auto generated using the [LALRPOP](https://github.com/nikomatsakis/lalrpop) library. In order to modify the parser, you can modify the grammar rules in `expression_parser.lalrpop`. 34 | 35 | Files: `mod.rs`, `expression_parser.rs`, `expression_parser.lalrpop` 36 | 37 | ### `src/weakest_precondition` 38 | The brains behind generating a weakest precondition. This file generates the weakest precondition `wp` in `expression` format from MIR statements of the user's code. The `gen()` function performs a recursive depth-first search on the MIR control-flow graph, performing necessary replacements in `wp`'s expression in reverse order. For example, a post-condition `return: i32 == (x: i32 + 5i32)` would be the `wp` when `gen()` returns from the exit point of a function. If the next MIR statement prior to the exit point of the function is `return = (tmp1: i32)`, then a replacement occurs with the result being `tmp1: i32 == (x: i32 + 5i32)`. This continues until all MIR statements are read, ending with the first MIR statement of the function. 39 | 40 | Additionally there is the file `overflow.rs`: this file contains functions for overflow checking. If an expression contains the binary operator `signed add`, then an additional set of expressions is added onto `wp` to check for overflow. 41 | 42 | Files: `mod.rs`, `overflow.rs` 43 | 44 | ### `src/smt_output` 45 | This module translates `wp` from the internal `expression` format to SMT-lib format that is used by the SMT solver Z3. It then sends the final expression to [libsmt.rs](https://github.com/Rust-Proof/libsmt.rs), which runs Z3 in a child process, and returns any relevant information to the user. 46 | 47 | Files: `mod.rs` 48 | 49 | ### `src/reporting` 50 | rustproof's internal reporting module. This allows developers to throw meaningful rustproof warnings and errors to the user without exposing them to unhelpful rustproof internals. The macros are `rp_warn!()` and `rp_error!()`. 51 | 52 | Files: `mod.rs` 53 | 54 | ### `src/tests` 55 | This is where unit tests live, as well as where system tests are called from. Unit tests for most modules, namely `weakest_precondition`, are not only difficult to do (such as creating MIR stubs) but are also redundant with thorough system tests. `system_tests.rs` creates child processes that run the system tests located in the root directory's `tests` folder. Each function in a system test must begin with `valid` or `invalid` to correspond with their expected return, and is compared with their actual output to see if the test fails or passes. While this method currently works, it does not work with versions of rust nightly after 2016-08-11. Ultimately this method of system testing must change. 56 | 57 | Files: `mod.rs`, `system_tests.rs`, `test_expression.rs` 58 | 59 | ### `lib.rs` 60 | This file is the first point of entry for rustproof. It contains the `#[plugin_registrar]` attribute that rustc uses to identify compiler plugins. The `fn registrar()` is called once during compilation to register the used plugins, which in this case is the `MirPass`. `MirPass` is called once per user function (which may need to change in order to implement verification of function calls in user code). `MirPass` collects information about the function and its MIR statements, calls `gen()` from `weakest_preconditon`, and finishes with calling `gen_smtlib()` from `smt_output`. 61 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | Michael Salter, Matthew Slocum, Vincent Schuster, 3 | Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | 5 | Licensed under the Apache License, Version 2.0 6 | or the MIT 8 | license , 9 | at your option. All files in the project carrying such 10 | notice may not be copied, modified, or distributed except 11 | according to those terms. 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustproof" 3 | version = "0.1.0" 4 | authors = [ 5 | "Matthew Slocum ", 6 | "Sami Sahli ", 7 | "Vincent Schuster ", 8 | "Matthew O'Brien ", 9 | "Michael Salter ", 10 | "Bradley Rasmussen ", 11 | "Drew Gohman " 12 | ] 13 | license = "Apache-2.0/MIT" 14 | description = "Compiler plugin to generate verification conditions from Rust code." 15 | repository = "https://github.com/Rust-Proof/rustproof" 16 | readme = "README.md" 17 | keywords = ["plugin", "smt", "verification", "solver", "satisfiability"] 18 | exclude = [ 19 | "documents/*", 20 | "scripts/*", 21 | "build.rs", 22 | ".travis.yml", 23 | "src/parser/expression_parser.lalrpop", 24 | ] 25 | 26 | #build = "build.rs" 27 | 28 | [build-dependencies] 29 | lalrpop = "0.11.0" 30 | 31 | [dependencies] 32 | env_logger = "0.3.4" 33 | lalrpop-util = "0.11.0" 34 | rustproof-libsmt = "0.1.0" 35 | petgraph = "0.2.7" 36 | 37 | [lib] 38 | name = "rustproof" 39 | path = "src/lib.rs" 40 | plugin = true 41 | -------------------------------------------------------------------------------- /EXAMPLES.md: -------------------------------------------------------------------------------- 1 | # Example Usages 2 | 3 | ## Integer Arithmetic with Overflow Checking 4 | ``` 5 | // Proven to never overflow 6 | #[condition(pre="(x:i32 <= i32::MAX - 5:i32)", post="return:i32 == (x:i32 + 5:i32)")] 7 | fn add_five(x:i32) -> i32 { 8 | assert!(x <= 2147483647-5); 9 | x+5 10 | } 11 | ``` 12 | 13 | ## Boolean Conditional 14 | ``` 15 | // Proven to always return the NOT of the provided argument 16 | #[condition(pre="true", post="(x:bool==true IMPLIES return:bool==false) && (x:bool==false IMPLIES return:bool==true)")] 17 | fn boolean_not(x:bool) -> bool { 18 | if x == true { 19 | false 20 | } else { 21 | true 22 | } 23 | } 24 | ``` 25 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Rust-Proof 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 The Rust-Proof Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rustproof 2 | 3 | [![Build Status](https://travis-ci.org/Rust-Proof/rustproof.svg?branch=master)](https://travis-ci.org/Rust-Proof/rustproof) 4 | 5 | Rustproof is a compiler plugin for the Rust programming language. It generates verification conditions for functions with supplied preconditions(`P`) and postconditions(`Q`). That is, given a supplied postcondition on a function, rustproof uses [predicate transformer semantics](https://en.wikipedia.org/wiki/Predicate_transformer_semantics) to generate a weakest precondition(`WP(S, Q)`) from the postcondition and a body of statements(`S`). The verification condition `P->WP(S,Q)` is then checked for validity by a SMT solver ([z3](https://github.com/Z3Prover/z3)). This process results in a proof of function correctness. 6 | 7 | ## Dependencies 8 | 9 | * `rustc 1.12.0-nightly (2016-08-12)`. 10 | 11 | * [z3](https://github.com/Z3Prover/z3) 12 | 13 | Your installation of z3 needs to be in your PATH for rustproof to work. 14 | 15 | ## Supported Rust Language Features 16 | 17 | * Integer arithmetic 18 | * `isize` and `usize` are **unsupported** 19 | * Boolean expressions, variables, and literals 20 | * Assertions (integer/boolean) 21 | * `assert_eq!()` is **unsupported** 22 | * If statements 23 | 24 | 25 | ## Usage 26 | 27 | ### Including Rustproof in Your Project 28 | 29 | Add rustproof as a dependency in `Cargo.toml` 30 | ```toml 31 | [dependencies] 32 | rustproof = { git = "https://github.com/Rust-Proof/rustproof.git" } 33 | ``` 34 | 35 | ### Using Rustproof 36 | 37 | `#![plugin(rustproof)]` is required in each file where rustproof is used. Typically this is placed at the beginning of a file. 38 | 39 | Rustproof uses a custom attribute `condition` to allow declaring pre/postconditions on functions. 40 | 41 | The attribute is supplied as: 42 | `#[condition(pre=" ", post=" ")]` 43 | and must be supplied before a function definition. 44 | 45 | See [USAGE](USAGE.md) for a detailed explanation of the attribute system. 46 | 47 | See [EXAMPLES](EXAMPLES.md) for example functions with condition attributes. 48 | 49 | Additionally, `#![plugin(rustproof(debug))]` prints out basic blocks of each function annotated with `#[condition(..)]`, as well as a step-by-step view of generating the verification condition. 50 | 51 | 52 | ## Contributors 53 | [Matthew Slocum][slocum] 54 | [Sami Sahli][sahli] 55 | [Vincent Schuster][schuster] 56 | [Michael Salter][salter] 57 | [Bradley Rasmussen][rasmussen] 58 | [Drew Gohman][gohman] 59 | [Matthew O'Brien][obrien] 60 | 61 | [slocum]:https://github.com/arc3x 62 | [sahli]:https://github.com/ssahli 63 | [schuster]:https://github.com/VSchuster 64 | [salter]:https://github.com/salterm 65 | [rasmussen]:https://github.com/bajr 66 | [gohman]:https://github.com/found101 67 | [obrien]:https://github.com/obriematt 68 | 69 | ## Reporting Issues 70 | 71 | Please report all issues on the github [issue tracker][issues]. 72 | 73 | [issues]:https://github.com/Rust-Proof/rustproof/issues 74 | 75 | 76 | ## License 77 | 78 | Rustproof is distributed under the terms of both the MIT license and the Apache License (Version 2.0). 79 | 80 | See [LICENSE-APACHE][1], [LICENSE-MIT][2], and [COPYRIGHT][3] for details. 81 | 82 | [1]:https://github.com/Rust-Proof/rustproof/blob/master/LICENSE-APACHE 83 | [2]:https://github.com/Rust-Proof/rustproof/blob/master/LICENSE-MIT 84 | [3]:https://github.com/Rust-Proof/rustproof/blob/master/COPYRIGHT 85 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | # To Use Rust-Proof 2 | Users should place the "condition" attribute tag above any function they should wish to test, along with a precondition and postcondition. If a user has a function "foo()" they can specify that Rust-Proof should analyze it like so: 3 | 4 | ``` 5 | #condition[pre="P",post="Q"] 6 | foo() { ... } 7 | ``` 8 | 9 | ...where "P" is a string containing the precondition, and "Q" is a string containing the postcondition. Neither string may be empty. If a user does not wish to specify either, they can enter the string "true". 10 | 11 | # How to format preconditions and postconditions 12 | Pre- and postconditions are made of boolean logical expressions. These expressions are composed of operands and operators. When all is said and done, the expressions should resolve to a boolean value. 13 | 14 | ## Operands 15 | Currently Rustproof will accept boolean literals (`true`, `false`), rust integer types (`u8`, `i64`, etc. excepting `isize` and `usize`), and variables of any of those types. Variables are named just like Rust identifiers, except for "return" which is a special variable only usable in the postcondition that refers to the return value of the function. 16 | Expressions can also be operands, if they resolve to the correct type. You must identify the type of your literal or variable with Rust-like syntax (except for "true" or "false"). Casting is not supported. 17 | In the precondition, the user can only reference variables that are arguments to the function in question. In the postcondition, one can reference arguments of the function and/or the special "return" variable, mentioned above. 18 | 19 | ## Operators 20 | There are three ways to think about operators: how many operands they work on, what types of operands they can work with, and what type an expression involving them resolves to. There are operator precendence rules (more on that below), but you may find that grouping expressions with parentheses is helpful. 21 | 22 | | Operator | Name | Number of operands | Operand type | Resolution type | 23 | |----------|-----------------------------|--------------------|---------------|-----------------| 24 | | + | Addition | 2 | Integer | Integer | 25 | | - | Subtraction | 2 | Integer | Integer | 26 | | * | Multiplication | 2 | Integer | Integer | 27 | | / | Division | 2 | Integer | Integer | 28 | | % | Modulus / Remainder | 2 | Integer | Integer | 29 | | \| | Bitwise Or | 2 | Any Primitive | Any Primitive | 30 | | & | Bitwise And | 2 | Any Primitive | Any Primitive | 31 | | ^ | Bitwise Exclusive Or / XOR | 2 | Any Primitive | Any Primitive | 32 | | << | Bitwise Left Shift | 2 | Any Primitive | Any Primitive | 33 | | >> | Bitwise Right Shift | 2 | Any Primitive | Any Primitive | 34 | | < | Less Than | 2 | Integer | Boolean | 35 | | <= | Less Than Or Equal | 2 | Integer | Boolean | 36 | | > | Greater Than | 2 | Integer | Boolean | 37 | | >= | Greater Than Or Equal | 2 | Integer | Boolean | 38 | | == | Equality | 2 | Integer | Boolean | 39 | | != | Inequality | 2 | Integer | Boolean | 40 | | && | Logical And | 2 | Boolean | Boolean | 41 | | \|\| | Logical Or | 2 | Boolean | Boolean | 42 | | AND | Conjunction | 2 | Boolean | Boolean | 43 | | OR | Disjunction | 2 | Boolean | Boolean | 44 | | XOR | Exclusive Disjunction / XOR | 2 | Boolean | Boolean | 45 | | IMPLIES | Implication | 2 | Boolean | Boolean | 46 | | => | Implication | 2 | Boolean | Boolean | 47 | | EQUIV | Equivalence | 2 | Boolean | Boolean | 48 | | <=> | Equivalence | 2 | Boolean | Boolean | 49 | | - | Negation | 1 | Integer | Integer | 50 | | ! | Bitwise Not | 1 | Any Primitive | Any Primitive | 51 | | NOT | Logical Negation | 1 | Boolean | Boolean | 52 | 53 | __Note__: The "&&", "||", and "!" operators are treated identically to the "AND", "OR", and "NOT" operators, respectively. "AND" and "OR" are added as conventions to make clear what is and is not meant to be a Rust-like expression, and "!" is overriden in Rust to be both logical and bitwise negation, since bitwise negation on a boolean primitive type amounts to the same thing. "IMPLIES" is a synonym for "=>", and "EQUIV" is a synonym for "<=>". 54 | 55 | __Operator precedence is as follows__ (more tightly binding first): 56 | ( ) 57 | - (Unary), !, NOT 58 | *, /, % 59 | +, - (Binary) 60 | ^, &, |, <<, >> 61 | &&, || 62 | AND, OR, XOR, IMPLIES, =>, EQUIV, <=> 63 | 64 | __Examples__: 65 | 66 | ``` 67 | x: i32 > 5: i32 68 | ! true 69 | (y: u64 <= b: u64) OR ((b: u64 + 4: u64) > 8: u64) 70 | return: bool AND (!false) 71 | ``` 72 | 73 | See [EXAMPLES](EXAMPLES.md) for example functions with condition attributes. 74 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop; 2 | 3 | fn main() { 4 | lalrpop::process_root().unwrap(); 5 | } -------------------------------------------------------------------------------- /documents/ProjectPlan.md: -------------------------------------------------------------------------------- 1 | Click the link for the Project Plan task list: 2 | 3 | https://docs.google.com/spreadsheets/d/1YVubjOM2RETHxuXW5PW8P6Mw0DGE8zytd-30QftTTwg/pubhtml 4 | -------------------------------------------------------------------------------- /documents/RequirementsSpecificationDocument_v0.5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Proof/rustproof/654b0043d2e45129bff17a56adcbf3f689471477/documents/RequirementsSpecificationDocument_v0.5.pdf -------------------------------------------------------------------------------- /documents/RequirementsSpecificationDocument_v1.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Proof/rustproof/654b0043d2e45129bff17a56adcbf3f689471477/documents/RequirementsSpecificationDocument_v1.0.pdf -------------------------------------------------------------------------------- /documents/RequirementsSpecificationDocument_v1.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Proof/rustproof/654b0043d2e45129bff17a56adcbf3f689471477/documents/RequirementsSpecificationDocument_v1.1.pdf -------------------------------------------------------------------------------- /documents/RequirementsSpecificationDocumentv1.2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Proof/rustproof/654b0043d2e45129bff17a56adcbf3f689471477/documents/RequirementsSpecificationDocumentv1.2.pdf -------------------------------------------------------------------------------- /documents/RequirementsSpecificationDocumentv1.3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Proof/rustproof/654b0043d2e45129bff17a56adcbf3f689471477/documents/RequirementsSpecificationDocumentv1.3.pdf -------------------------------------------------------------------------------- /documents/RiskManagementPlan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Proof/rustproof/654b0043d2e45129bff17a56adcbf3f689471477/documents/RiskManagementPlan.pdf -------------------------------------------------------------------------------- /documents/SoftwareArchitectureDesignDocument_v1.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Proof/rustproof/654b0043d2e45129bff17a56adcbf3f689471477/documents/SoftwareArchitectureDesignDocument_v1.0.pdf -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Running Examples 2 | ```sh 3 | $ cargo run --example 4 | ``` 5 | ```sh 6 | $ cargo run --example temp 7 | ``` 8 | -------------------------------------------------------------------------------- /scripts/travis-z3.sh: -------------------------------------------------------------------------------- 1 | # Copyright Takayuki Muranushi (c) 2015 2 | 3 | # All rights reserved. 4 | 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are met: 7 | 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | 11 | # * Redistributions in binary form must reproduce the above 12 | # copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided 14 | # with the distribution. 15 | 16 | # * Neither the name of Takayuki Muranushi nor the names of other 17 | # contributors may be used to endorse or promote products derived 18 | # from this software without specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | 33 | 34 | #!/bin/bash 35 | 36 | mkdir -p ~/.local/bin 37 | mkdir -p ~/.local/lib 38 | wget https://github.com/Z3Prover/z3/releases/download/z3-4.4.1/z3-4.4.1-x64-ubuntu-14.04.zip 39 | unzip z3-4.4.1-x64-ubuntu-14.04.zip 40 | cp z3-4.4.1-x64-ubuntu-14.04/bin/z3 ~/.local/bin 41 | cp z3-4.4.1-x64-ubuntu-14.04/bin/*.so ~/.local/lib 42 | -------------------------------------------------------------------------------- /src/expression/mod.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Internal data structure and related functions to represent logical expressions. 12 | 13 | //#[macro_use] 14 | extern crate term; 15 | use std::fmt; 16 | 17 | // Boolean Expression type 18 | #[derive(Clone, PartialEq)] 19 | pub enum Expression { 20 | // Two sub-expressions joined by an operator 21 | BinaryExpression(BinaryExpressionData), 22 | // A sub-expression acted upon by an operator 23 | UnaryExpression(UnaryExpressionData), 24 | // A variable; should be either one of a function's formal arguments, 25 | // a special "return" variable, or something from an encapsulating scope. 26 | VariableMapping(VariableMappingData), 27 | // Boolean literals 28 | BooleanLiteral(bool), 29 | // Integer literals 30 | UnsignedBitVector(UnsignedBitVectorData), 31 | SignedBitVector(SignedBitVectorData) 32 | } 33 | 34 | // Used for representing Expression types as strings, recursively. 35 | impl fmt::Display for Expression { 36 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 37 | match *self { 38 | Expression::BinaryExpression (ref b) => { 39 | write!(f, "({} {} {})", *b.left, b.op, *b.right) 40 | }, 41 | Expression::UnaryExpression (ref u) => write!(f, "({} {})", u.op, *u.e), 42 | Expression::VariableMapping (ref v) => write!(f, "({}: {})", v.name, v.var_type), 43 | Expression::BooleanLiteral (ref b) => write!(f, "({})", b), 44 | Expression::UnsignedBitVector(ref u) => { 45 | write!(f, "({}u{})", u.value, u.size.to_string()) 46 | }, 47 | Expression::SignedBitVector(ref s) => { 48 | write!(f, "({}i{})", s.value, s.size.to_string()) 49 | } 50 | } 51 | } 52 | } 53 | 54 | impl fmt::Debug for Expression { 55 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 56 | write!(f, "{}", self) 57 | } 58 | } 59 | 60 | #[derive(Clone, PartialEq)] 61 | pub struct BinaryExpressionData { 62 | pub op: BinaryOperator, 63 | pub left: Box, 64 | pub right: Box 65 | } 66 | 67 | #[derive(Clone, PartialEq)] 68 | pub struct UnaryExpressionData { 69 | pub op: UnaryOperator, 70 | pub e: Box 71 | } 72 | 73 | #[derive(Clone, Debug)] 74 | pub struct VariableMappingData { pub name: String, pub var_type: Types } 75 | 76 | // Check equality for VariableMappingData types. 77 | // Should return true if the name and type of the variables are the same. 78 | impl PartialEq for VariableMappingData { 79 | fn eq(&self, _rhs: &VariableMappingData) -> bool { 80 | return (self.name == _rhs.name) && (self.var_type == _rhs.var_type); 81 | } 82 | } 83 | 84 | // Ensures it is clear that VariableMappingData has full equality. 85 | impl Eq for VariableMappingData {} 86 | 87 | impl fmt::Display for VariableMappingData { 88 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 89 | write!(f, "({}: {})", self.name, self.var_type) 90 | } 91 | } 92 | 93 | #[derive(Clone, PartialEq)] 94 | pub struct UnsignedBitVectorData { 95 | pub size: u8, 96 | pub value: u64, 97 | } 98 | 99 | #[derive(Clone, PartialEq)] 100 | pub struct SignedBitVectorData { 101 | pub size: u8, 102 | pub value: i64, 103 | } 104 | 105 | #[derive(Clone, PartialEq)] 106 | pub enum BinaryOperator { 107 | // Normal operators 108 | Addition, 109 | Subtraction, 110 | Multiplication, 111 | Division, 112 | Modulo, 113 | // Overflow operators 114 | SignedMultiplicationDoesNotOverflow, 115 | SignedMultiplicationDoesNotUnderflow, 116 | UnsignedMultiplicationDoesNotOverflow, 117 | // Bitwise operators 118 | BitwiseOr, 119 | BitwiseAnd, 120 | BitwiseXor, 121 | BitwiseLeftShift, 122 | BitwiseRightShift, 123 | // Comparison operators 124 | LessThan, 125 | LessThanOrEqual, 126 | GreaterThan, 127 | GreaterThanOrEqual, 128 | Equal, 129 | NotEqual, 130 | // Boolean logical operators 131 | And, 132 | Or, 133 | Xor, 134 | Implication, 135 | BiImplication, 136 | } 137 | 138 | impl fmt::Display for BinaryOperator { 139 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 140 | match *self { 141 | BinaryOperator::Addition => write!(f, "+"), 142 | BinaryOperator::Subtraction => write!(f, "-"), 143 | BinaryOperator::Multiplication => write!(f, "*"), 144 | BinaryOperator::Division => write!(f, "/"), 145 | BinaryOperator::Modulo => write!(f, "%"), 146 | BinaryOperator::SignedMultiplicationDoesNotOverflow => { 147 | write!(f, "s_mul_no_overflow") 148 | }, 149 | BinaryOperator::SignedMultiplicationDoesNotUnderflow => { 150 | write!(f, "s_mul_no_underflow") 151 | }, 152 | BinaryOperator::UnsignedMultiplicationDoesNotOverflow => { 153 | write!(f, "u_mul_no_overflow") 154 | }, 155 | BinaryOperator::BitwiseOr => write!(f, "|"), 156 | BinaryOperator::BitwiseAnd => write!(f, "&"), 157 | BinaryOperator::BitwiseXor => write!(f, "^"), 158 | BinaryOperator::BitwiseLeftShift => write!(f, "<<"), 159 | BinaryOperator::BitwiseRightShift => write!(f, ">>"), 160 | BinaryOperator::LessThan => write!(f, "<"), 161 | BinaryOperator::LessThanOrEqual => write!(f, "<="), 162 | BinaryOperator::GreaterThan => write!(f, ">"), 163 | BinaryOperator::GreaterThanOrEqual => write!(f, ">="), 164 | BinaryOperator::Equal => write!(f, "=="), 165 | BinaryOperator::NotEqual => write!(f, "!="), 166 | BinaryOperator::And => write!(f, "AND"), 167 | BinaryOperator::Or => write!(f, "OR"), 168 | BinaryOperator::Xor => write!(f, "XOR"), 169 | BinaryOperator::Implication => write!(f, "IMPLIES"), 170 | BinaryOperator::BiImplication => write!(f, "EQUIV"), 171 | } 172 | } 173 | } 174 | 175 | #[derive(Clone, PartialEq)] 176 | pub enum UnaryOperator { 177 | Negation, 178 | BitwiseNot, 179 | // Boolean logical operator 180 | Not, 181 | } 182 | 183 | impl fmt::Display for UnaryOperator { 184 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 185 | match *self { 186 | UnaryOperator::Negation => { write!(f, "-") }, 187 | UnaryOperator::BitwiseNot => { write!(f, "!") }, 188 | UnaryOperator::Not => { write!(f, "NOT") } 189 | } 190 | } 191 | } 192 | 193 | #[derive(Clone, PartialEq, Debug)] 194 | pub enum Types { 195 | Bool, 196 | I8, 197 | I16, 198 | I32, 199 | I64, 200 | U8, 201 | U16, 202 | U32, 203 | U64, 204 | Void 205 | } 206 | 207 | impl fmt::Display for Types { 208 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 209 | match *self { 210 | Types::Bool => { write!(f, "bool") }, 211 | Types::I8 => { write!(f, "i8") }, 212 | Types::I16 => { write!(f, "i16") }, 213 | Types::I32 => { write!(f, "i32") }, 214 | Types::I64 => { write!(f, "i64") }, 215 | Types::U8 => { write!(f, "u8") }, 216 | Types::U16 => { write!(f, "u16") }, 217 | Types::U32 => { write!(f, "u32") }, 218 | Types::U64 => { write!(f, "u64") }, 219 | Types::Void => { write!(f, "()") }, 220 | } 221 | } 222 | } 223 | 224 | /// Recurses through an Expression and replaces any instance of a variable with a given Expression. 225 | /// 226 | /// # Arguments: 227 | /// * `source_expression` - The Expression to be recursed through. 228 | /// * `target` - The variable to be replaced. 229 | /// * `replacement` - The Expression to replace the target, if found. 230 | /// 231 | /// # Remarks: 232 | /// 233 | pub fn substitute_variable_with_expression (source_expression: &mut Expression, 234 | target: &VariableMappingData, 235 | replacement: &Expression) { 236 | let mut replace: bool = false; 237 | match source_expression { 238 | &mut Expression::BinaryExpression(ref mut b) => { 239 | // Recurisvely call the sub-expressions 240 | substitute_variable_with_expression(&mut(*b.left), target, replacement); 241 | substitute_variable_with_expression(&mut(*b.right), target, replacement); 242 | }, 243 | &mut Expression::UnaryExpression(ref mut u) => { 244 | // Recurisvely call the sub-expression 245 | substitute_variable_with_expression(&mut(*u.e), target, replacement); 246 | }, 247 | &mut Expression::VariableMapping(ref mut v) => { 248 | // Substitute the variable if it matches the target 249 | if v == target { 250 | replace = true; 251 | } 252 | }, 253 | _ => { 254 | // No substitution should be done 255 | } 256 | } 257 | 258 | // Substitute the variable after the match to avoid scope issues 259 | if replace { 260 | *source_expression = replacement.clone(); 261 | } 262 | } 263 | 264 | /// Recurses through an Expression and returns the type it would evaluate to. 265 | /// 266 | /// # Arguments: 267 | /// * `expression` - An Expression whose evaluation type will be returned. 268 | /// 269 | /// # Return: 270 | /// * A String representation of the type that should return from the top level of the Expression. 271 | /// 272 | /// # Remarks: 273 | /// 274 | pub fn determine_evaluation_type ( expression: &Expression ) -> Types { 275 | match ty_check(expression) { 276 | Ok(_) => { 277 | match *expression { 278 | Expression::BinaryExpression(ref b) => { 279 | match b.op { 280 | BinaryOperator::Addition 281 | | BinaryOperator::Subtraction 282 | | BinaryOperator::Multiplication 283 | | BinaryOperator::Division 284 | | BinaryOperator::Modulo 285 | | BinaryOperator::BitwiseLeftShift 286 | | BinaryOperator::BitwiseRightShift 287 | | BinaryOperator::BitwiseOr 288 | | BinaryOperator::BitwiseAnd 289 | | BinaryOperator::BitwiseXor => determine_evaluation_type(&*b.left), 290 | BinaryOperator::LessThan 291 | | BinaryOperator::LessThanOrEqual 292 | | BinaryOperator::GreaterThan 293 | | BinaryOperator::GreaterThanOrEqual 294 | | BinaryOperator::SignedMultiplicationDoesNotOverflow 295 | | BinaryOperator::SignedMultiplicationDoesNotUnderflow 296 | | BinaryOperator::UnsignedMultiplicationDoesNotOverflow 297 | | BinaryOperator::Equal 298 | | BinaryOperator::NotEqual 299 | | BinaryOperator::And 300 | | BinaryOperator::Or 301 | | BinaryOperator::Xor 302 | | BinaryOperator::Implication 303 | | BinaryOperator::BiImplication => Types::Bool, 304 | } 305 | }, 306 | Expression::UnaryExpression(ref u) => { 307 | match u.op { 308 | UnaryOperator::Negation 309 | | UnaryOperator::Not => determine_evaluation_type(&*u.e), 310 | UnaryOperator::BitwiseNot => determine_evaluation_type(&*u.e), 311 | } 312 | }, 313 | Expression::VariableMapping(ref v) => v.var_type.clone(), 314 | Expression::BooleanLiteral(_) => Types::Bool, 315 | Expression::UnsignedBitVector(ref u) => { 316 | match u.size { 317 | 8 => Types::U8, 318 | 16 => Types::U16, 319 | 32 => Types::U32, 320 | 64 => Types::U64, 321 | _ => { 322 | rp_error!( 323 | "Invalid or Unsupported integer type: \"u{}\"", 324 | u.size.to_string() 325 | ); 326 | } 327 | } 328 | }, 329 | Expression::SignedBitVector(ref s) => { 330 | match s.size { 331 | 8 => Types::I8, 332 | 16 => Types::I16, 333 | 32 => Types::I32, 334 | 64 => Types::I64, 335 | _ => { 336 | rp_error!( 337 | "Invalid or Unsupported integer type: \"i{}\"", 338 | s.size.to_string() 339 | ); 340 | } 341 | } 342 | } 343 | } 344 | }, 345 | Err(e) => rp_error!("{}", e), 346 | } 347 | } 348 | 349 | /// Recurses through an Expression and checks for validity of types, operands, and integer bounds. 350 | /// 351 | /// # Arguments: 352 | /// * `expression` - An Expression whose type will be checked. 353 | /// 354 | /// # Return: 355 | /// * Ok(true) if all seems valid. 356 | /// * Err(String) otherwise, the String containing a message about the first problem encountered. 357 | /// 358 | /// # Remarks: 359 | /// 360 | pub fn ty_check( expression: &Expression ) -> Result { 361 | match *expression { 362 | Expression::BinaryExpression(ref b) => { 363 | match b.op { 364 | BinaryOperator::Addition 365 | | BinaryOperator::Subtraction 366 | | BinaryOperator::Multiplication 367 | | BinaryOperator::Division 368 | | BinaryOperator::Modulo 369 | | BinaryOperator::SignedMultiplicationDoesNotOverflow 370 | | BinaryOperator::SignedMultiplicationDoesNotUnderflow 371 | | BinaryOperator::UnsignedMultiplicationDoesNotOverflow => { 372 | match ty_check(&*b.left) { 373 | Ok(_) => { 374 | match ty_check(&*b.right) { 375 | Ok(_) => { 376 | let l_type: Types = determine_evaluation_type(&*b.left); 377 | let r_type: Types = determine_evaluation_type(&*b.right); 378 | // Ensure both operands are numeric types 379 | if (l_type == Types::Bool) || (r_type == Types::Bool) { 380 | Err( 381 | format!( 382 | "Invalid use of binary operator {} on boolean \ 383 | value(s)", 384 | b.op 385 | ) 386 | ) 387 | // Ensure both operand types match 388 | } else if l_type != r_type { 389 | Err( 390 | format!( 391 | "Binary operand types do not match: {} {} {}", 392 | l_type, 393 | b.op, 394 | r_type 395 | ) 396 | ) 397 | } else { 398 | Ok(true) 399 | } 400 | }, 401 | Err(e) => Err(e) 402 | } 403 | }, 404 | Err(e) => Err(e) 405 | } 406 | }, 407 | BinaryOperator::BitwiseLeftShift | BinaryOperator::BitwiseRightShift => { 408 | match ty_check(&*b.left) { 409 | Ok(_) => { 410 | match ty_check(&*b.right) { 411 | Ok(_) => { 412 | let l_type: Types = determine_evaluation_type(&*b.left); 413 | let r_type: Types = determine_evaluation_type(&*b.right); 414 | // Ensure both operands are numeric types 415 | if (l_type == Types::Bool) || (r_type == Types::Bool) { 416 | Err( 417 | format!( 418 | "Invalid use of binary operator {} on boolean \ 419 | value(s)", 420 | b.op 421 | ) 422 | ) 423 | //Ensure both operand types are of same signedness 424 | } else if (is_valid_signed(&*b.left) 425 | && !is_valid_signed(&*b.right)) 426 | || (is_valid_unsigned(&*b.left) 427 | && !is_valid_unsigned(&*b.right)) { 428 | Err( 429 | format!( 430 | "Binary operand types do not match: {} {} {}", 431 | l_type, 432 | b.op, 433 | r_type 434 | ) 435 | ) 436 | } else { 437 | Ok(true) 438 | } 439 | }, 440 | Err(e) => Err(e) 441 | } 442 | }, 443 | Err(e) => Err(e) 444 | } 445 | }, 446 | BinaryOperator::BitwiseOr 447 | | BinaryOperator::BitwiseAnd 448 | | BinaryOperator::BitwiseXor => { 449 | match ty_check(&*b.left) { 450 | Ok(_) => { 451 | match ty_check(&*b.right) { 452 | Ok(_) => { 453 | let l_type: Types = determine_evaluation_type(&*b.left); 454 | let r_type: Types = determine_evaluation_type(&*b.right); 455 | // Ensure both operand types match 456 | if l_type != r_type { 457 | Err( 458 | format!( 459 | "Binary operand types do not match: {} {} {}", 460 | l_type, 461 | b.op, 462 | r_type 463 | ) 464 | ) 465 | } else { 466 | Ok(true) 467 | } 468 | }, 469 | Err(e) => Err(e) 470 | } 471 | }, 472 | Err(e) => Err(e) 473 | } 474 | }, 475 | BinaryOperator::LessThan 476 | | BinaryOperator::LessThanOrEqual 477 | | BinaryOperator::GreaterThan 478 | | BinaryOperator::GreaterThanOrEqual => { 479 | match ty_check(&*b.left) { 480 | Ok(_) => { 481 | match ty_check(&*b.right) { 482 | Ok(_) => { 483 | let l_type: Types = determine_evaluation_type(&*b.left); 484 | let r_type: Types = determine_evaluation_type(&*b.right); 485 | // Ensure both operands are numeric types 486 | if (l_type == Types::Bool) || (r_type == Types::Bool) { 487 | Err( 488 | format!( 489 | "Invalid use of binary operator {} on boolean \ 490 | value(s)", 491 | b.op 492 | ) 493 | ) 494 | // Ensure both operand types match 495 | } else if l_type != r_type { 496 | Err( 497 | format!( 498 | "Binary operand types do not match: {} {} {}", 499 | l_type, 500 | b.op, 501 | r_type 502 | ) 503 | ) 504 | } else { 505 | Ok(true) 506 | } 507 | }, 508 | Err(e) => Err(e) 509 | } 510 | }, 511 | Err(e) => Err(e) 512 | } 513 | }, 514 | BinaryOperator::Equal | BinaryOperator::NotEqual => { 515 | match ty_check(&*b.left) { 516 | Ok(_) => { 517 | match ty_check(&*b.right) { 518 | Ok(_) => { 519 | let l_type: Types = determine_evaluation_type(&*b.left); 520 | let r_type: Types = determine_evaluation_type(&*b.right); 521 | // Ensure both operand types match 522 | if l_type != r_type { 523 | Err( 524 | format!( 525 | "Binary operand types do not match: {} {} {}", 526 | l_type, 527 | b.op, 528 | r_type 529 | ) 530 | ) 531 | } else { 532 | Ok(true) 533 | } 534 | }, 535 | Err(e) => Err(e) 536 | } 537 | }, 538 | Err(e) => Err(e) 539 | } 540 | }, 541 | BinaryOperator::And | BinaryOperator::Or | BinaryOperator::Xor 542 | | BinaryOperator::Implication | BinaryOperator::BiImplication => { 543 | match ty_check(&*b.left) { 544 | Ok(_) => { 545 | match ty_check(&*b.right) { 546 | Ok(_) => { 547 | let l_type: Types = determine_evaluation_type(&*b.left); 548 | let r_type: Types = determine_evaluation_type(&*b.right); 549 | // Ensure both operands are boolean types 550 | if (l_type != Types::Bool) || (r_type != Types::Bool) { 551 | Err( 552 | format!( 553 | "Invalid use of binary operator {} on numeric value(s)", 554 | b.op 555 | ) 556 | ) 557 | // Ensure both operand types match 558 | } else if l_type != r_type { 559 | Err( 560 | format!( 561 | "Binary operand types do not match: {} {} {}", 562 | l_type, 563 | b.op, 564 | r_type 565 | ) 566 | ) 567 | } else { 568 | Ok(true) 569 | } 570 | }, 571 | Err(e) => Err(e) 572 | } 573 | }, 574 | Err(e) => Err(e) 575 | } 576 | } 577 | } 578 | }, 579 | Expression::UnaryExpression(ref u) => { 580 | match u.op { 581 | UnaryOperator::Negation => { 582 | match ty_check(&*u.e) { 583 | Ok(_) => { 584 | let e_type: Types = determine_evaluation_type(&*u.e); 585 | 586 | // Ensure operand is a numeric type 587 | if e_type == Types::Bool { 588 | Err( 589 | format!( 590 | "Invalid use of operator {} on boolean value {}", 591 | u.op, 592 | *u.e 593 | ) 594 | ) 595 | // Ensure operand is not an unsigned type 596 | } else if is_valid_unsigned(&*u.e) { 597 | Err( 598 | format!( 599 | "Invalid use of operator {} on unsigned value {}", 600 | u.op, 601 | *u.e 602 | ) 603 | ) 604 | } else { 605 | Ok(true) 606 | } 607 | }, 608 | Err(e) => Err(e) 609 | } 610 | }, 611 | UnaryOperator::BitwiseNot => { 612 | match ty_check(&*u.e) { 613 | Ok(_) => Ok(true), 614 | Err(e) => Err(e) 615 | } 616 | }, 617 | UnaryOperator::Not => { 618 | let e_type: Types = determine_evaluation_type(&*u.e); 619 | // Ensure operand is a boolean type 620 | if e_type != Types::Bool { 621 | Err( 622 | format!( 623 | "Invalid use of operator {} on non-boolean value {}", 624 | u.op, 625 | *u.e 626 | ) 627 | ) 628 | } else { 629 | Ok(true) 630 | } 631 | }, 632 | } 633 | }, 634 | Expression::VariableMapping(ref v) => { 635 | if let Types::Void = v.var_type { 636 | Err(format!("Variable {} has void type!", v.name)) 637 | } else { 638 | Ok(true) 639 | } 640 | }, 641 | Expression::BooleanLiteral(_) => { 642 | Ok(true) 643 | }, 644 | Expression::UnsignedBitVector(ref u) => { 645 | match u.size { 646 | 8 => { 647 | if (u.value >= u8::min_value() as u64) && (u.value <= u8::max_value() as u64) { 648 | Ok(true) 649 | } else { 650 | Err(format!("Out of range value for u8 type: {}", u.value.to_string())) 651 | } 652 | }, 653 | 16 => { 654 | if (u.value >= u16::min_value() as u64) 655 | && (u.value <= u16::max_value() as u64) { 656 | Ok(true) 657 | } else { 658 | Err(format!("Out of range value for u16 type: {}", u.value.to_string())) 659 | } 660 | }, 661 | 32 => { 662 | if (u.value >= u32::min_value() as u64) 663 | && (u.value <= u32::max_value() as u64) { 664 | Ok(true) 665 | } else { 666 | Err(format!("Out of range value for u32 type: {}", u.value.to_string())) 667 | } 668 | }, 669 | 64 => { 670 | if (u.value >= u64::min_value() as u64) 671 | && (u.value <= u64::max_value() as u64) { 672 | Ok(true) 673 | } else { 674 | Err(format!("Out of range value for u64 type: {}", u.value.to_string())) 675 | } 676 | }, 677 | _ => { 678 | Err(format!("Invalid or unsupported integer type: \"u{}\"", u.size.to_string())) 679 | } 680 | } 681 | }, 682 | Expression::SignedBitVector(ref s) => { 683 | match s.size { 684 | 8 => { 685 | if (s.value >= i8::min_value() as i64) && (s.value <= i8::max_value() as i64) { 686 | Ok(true) 687 | } else { 688 | Err(format!("Out of range value for i8 type: {}", s.value.to_string())) 689 | } 690 | }, 691 | 16 => { 692 | if (s.value >= i16::min_value() as i64) 693 | && (s.value <= i16::max_value() as i64) { 694 | Ok(true) 695 | } else { 696 | Err(format!("Out of range value for i16 type: {}", s.value.to_string())) 697 | } 698 | }, 699 | 32 => { 700 | if (s.value >= i32::min_value() as i64) 701 | && (s.value <= i32::max_value() as i64) { 702 | Ok(true) 703 | } else { 704 | Err(format!("Out of range value for i32 type: {}", s.value.to_string())) 705 | } 706 | }, 707 | 64 => { 708 | if (s.value >= i64::min_value() as i64) 709 | && (s.value <= i64::max_value() as i64) { 710 | Ok(true) 711 | } else { 712 | Err(format!("Out of range value for i64 type: {}", s.value.to_string())) 713 | } 714 | }, 715 | _ => { 716 | Err( 717 | format!( 718 | "Invalid or unsupported integer type: \"i{}\"", 719 | s.size.to_string() 720 | ) 721 | ) 722 | } 723 | } 724 | } 725 | } 726 | } 727 | 728 | /// Checks if an Expression matches one of the supported unsigned integer types 729 | /// 730 | /// # Arguments: 731 | /// * `e` - An Expression 732 | /// 733 | /// # Return: 734 | /// * `true` if it matches, `false` otherwise 735 | /// 736 | /// # Remarks: 737 | /// * Current supported types: u8, u16, u32, u64 738 | /// 739 | pub fn is_valid_unsigned(e: &Expression) -> bool { 740 | match *e { 741 | Expression::VariableMapping(ref v) => { 742 | is_unsigned_type(v.var_type.clone()) 743 | } 744 | Expression::UnsignedBitVector(ref u) => { 745 | match u.size { 746 | 8u8 | 16u8 | 32u8 | 64u8 => true, 747 | _ => false, 748 | } 749 | } 750 | _ => false, 751 | } 752 | } 753 | 754 | /// Checks if an Expression matches one of the supported signed integer types 755 | /// 756 | /// # Arguments: 757 | /// * `e` - An Expression 758 | /// 759 | /// # Return: 760 | /// * `true` if it matches, `false` otherwise 761 | /// 762 | /// # Remarks: 763 | /// * Current supported types: i8, i16, i32, i64 764 | /// 765 | pub fn is_valid_signed(e: &Expression) -> bool { 766 | match *e { 767 | Expression::VariableMapping(ref v) => { 768 | is_signed_type(v.var_type.clone()) 769 | } 770 | Expression::SignedBitVector(ref s) => { 771 | match s.size { 772 | 8u8 | 16u8 | 32u8 | 64u8 => true, 773 | _ => false, 774 | } 775 | } 776 | _ => false, 777 | } 778 | } 779 | 780 | /// Checks if a Types, like the return from determine_evaluation_type(), matches one of the 781 | /// supported unsigned integer types 782 | /// 783 | /// # Arguments: 784 | /// * `t` - A Types 785 | /// 786 | /// # Return: 787 | /// * `true` if it matches, `false` otherwise 788 | /// 789 | /// # Remarks: 790 | /// * Current supported types: u8, u16, u32, u64 791 | /// 792 | pub fn is_unsigned_type(t: Types) -> bool { 793 | match t { 794 | Types::U8 | Types::U16 | Types::U32 | Types::U64 => true, 795 | _ => false, 796 | } 797 | } 798 | 799 | /// Checks if a Types, like the return from determine_evaluation_type(), matches one of the 800 | /// supported signed integer types 801 | /// 802 | /// # Arguments: 803 | /// * `t` - A Types 804 | /// 805 | /// # Return: 806 | /// * `true` if it matches, `false` otherwise 807 | /// 808 | /// # Remarks: 809 | /// * Current supported types: i8, i16, i32, i64 810 | /// 811 | pub fn is_signed_type(t: Types) -> bool { 812 | match t { 813 | Types::I8 | Types::I16 | Types::I32 | Types::I64 => true, 814 | _ => false, 815 | } 816 | } 817 | 818 | /// Returns a `Types` interpreted from a `String` representing a variable or literal type 819 | /// 820 | /// # Arguments: 821 | /// * `s` - A String 822 | /// 823 | /// # Return: 824 | /// * A Types 825 | /// 826 | /// # Remarks: 827 | /// * Current supported types: bool, i8, i16, i32, i64, u8, u16, u32, u64 828 | pub fn string_to_type(s: String) -> Types { 829 | match s.as_str() { 830 | "bool" => Types::Bool, 831 | "i8" => Types::I8, 832 | "i16" => Types::I16, 833 | "i32" => Types::I32, 834 | "i64" => Types::I64, 835 | "u8" => Types::U8, 836 | "u16" => Types::U16, 837 | "u32" => Types::U32, 838 | "u64" => Types::U64, 839 | "()" => Types::Void, 840 | _ => unimplemented!(), 841 | } 842 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Rustproof is a compiler plugin for the Rust programming language. It generates verification 12 | //! conditions for functions with supplied preconditions(`P`) and postconditions(`Q`). That is, given a 13 | //! supplied postcondition on a function, rustproof uses [predicate transformer semantics](https://en.wikipedia.org/wiki/Predicate_transformer_semantics) 14 | //! to generate a weakest precondition(`WP`). The verification condition `P->WP` is then checked for 15 | //! satisfiability by a SMT solver ([z3](https://github.com/Z3Prover/z3)). 16 | //! This process results in a proof of function correctness. 17 | //! 18 | 19 | //! Access the rustproof README [here](https://github.com/Rust-Proof/rustproof/blob/master/README.md). 20 | //! 21 | //! The following documentation is intended for the devlopers of rustproof. 22 | //! These descibed modules are not user-facing. 23 | 24 | 25 | #![crate_type="dylib"] 26 | #![feature(plugin_registrar, rustc_private)] 27 | 28 | #[macro_use] extern crate syntax; 29 | #[macro_use] mod reporting; 30 | 31 | // External crate imports 32 | #[macro_use] extern crate rustproof_libsmt; 33 | // The following line is being weird to me sometimes 34 | #[macro_use] extern crate log; 35 | extern crate petgraph; 36 | extern crate rustc; 37 | extern crate rustc_plugin; 38 | extern crate rustc_data_structures; 39 | extern crate rustc_const_math; 40 | //extern crate syntax; 41 | extern crate rustc_errors; 42 | 43 | // External imports 44 | use rustc_data_structures::indexed_vec::Idx; 45 | use rustc_plugin::Registry; 46 | use rustc::mir::repr::{Mir, BasicBlock, BasicBlockData, Arg, Temp, Var, ArgDecl, TempDecl, VarDecl}; 47 | use rustc::mir::transform::{Pass, MirPass, MirSource}; 48 | use rustc::ty::{TyCtxt, FnOutput}; 49 | use syntax::feature_gate::AttributeType; 50 | use syntax::parse::token::InternedString; 51 | use syntax::ast::MetaItemKind; 52 | 53 | // Local imports 54 | use expression::*; 55 | use parser::*; 56 | use smt_output::*; 57 | use weakest_precondition::*; 58 | 59 | // rustproof modules 60 | mod expression; 61 | mod parser; 62 | mod smt_output; 63 | mod weakest_precondition; 64 | #[cfg(test)] 65 | mod tests; 66 | 67 | 68 | // Register plugin with compiler 69 | #[plugin_registrar] 70 | pub fn registrar(reg: &mut Registry) { 71 | // If debug is an argument, set the debug flag to true 72 | let mut debug = false; 73 | for arg in reg.args() { 74 | if arg.clone().unwrap().node == MetaItemKind::Word(InternedString::new("debug")) { 75 | debug = true; 76 | } 77 | else { 78 | rp_error!("unrecognized plugin argument"); 79 | } 80 | } 81 | 82 | let visitor = MirVisitor { debug: debug }; 83 | 84 | reg.register_attribute("condition".to_string(), AttributeType::Whitelisted); 85 | reg.register_mir_pass(Box::new(visitor)); 86 | } 87 | 88 | /// Represents the data from the MIR pass relevant to the function being analyzed 89 | /// 90 | 91 | /// # Purpose: 92 | /// * Used to pass data from the MIR and the computed weakest_precondition 93 | 94 | /// 95 | pub struct MirData<'tcx> { 96 | block_data: Vec<&'tcx BasicBlockData<'tcx>>, 97 | arg_data: Vec<&'tcx ArgDecl<'tcx>>, 98 | var_data: Vec<&'tcx VarDecl<'tcx>>, 99 | temp_data: Vec<&'tcx TempDecl<'tcx>>, 100 | func_return_type: Types, 101 | } 102 | 103 | // required struct for Pass impl 104 | struct MirVisitor { debug: bool } 105 | 106 | /// This must exist and must be blank 107 | impl <'tcx> Pass for MirVisitor {} 108 | 109 | /// Sets up the compiler to go through MIR code. 110 | /// 111 | /// # Remarks: 112 | impl <'tcx> MirPass<'tcx> for MirVisitor { 113 | // Visit the MIR of the entire program 114 | fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { 115 | let debug = self.debug; 116 | // Clear the stored attributes in the builder 117 | let mut pre_string = "".to_string(); 118 | let mut post_string = "".to_string(); 119 | let pre_expr; 120 | let post_expr; 121 | 122 | // Store relevant data 123 | let item_id = src.item_id(); 124 | let def_id = tcx.map.local_def_id(item_id); 125 | let name = tcx.item_path_str(def_id); 126 | let attrs = tcx.map.attrs(item_id); 127 | 128 | // TODO: Find a better way to do this 129 | for attr in attrs { 130 | parse_attribute(&mut pre_string, &mut post_string, attr); 131 | } 132 | 133 | // TODO: Find a better condition check 134 | if pre_string != "" { 135 | // Parse the pre- and postcondition arguments 136 | pre_expr = Some(parser::parse_condition(pre_string.as_str())); 137 | post_expr = Some(parser::parse_condition(post_string.as_str())); 138 | 139 | // Get the return type 140 | let func_return_type: String = match mir.return_ty { 141 | FnOutput::FnConverging(t) => { 142 | t.to_string() 143 | }, 144 | _ => unimplemented!(), 145 | }; 146 | 147 | // Struct to carry MIR data to later stages 148 | let mut data = MirData { 149 | block_data: Vec::new(), 150 | arg_data: Vec::new(), 151 | var_data: Vec::new(), 152 | temp_data: Vec::new(), 153 | func_return_type: string_to_type(func_return_type), 154 | }; 155 | 156 | // Get the basic block data 157 | for index in 0..mir.basic_blocks().len() { 158 | let block = BasicBlock::new(index); 159 | data.block_data.push(&mir[block]); 160 | } 161 | 162 | // Get the function argument declarations 163 | for index in 0..mir.arg_decls.len() { 164 | let arg = Arg::new(index); 165 | data.arg_data.push(&mir.arg_decls[arg]); 166 | } 167 | 168 | // Get the temp declarations 169 | for index in 0..mir.temp_decls.len() { 170 | let temp = Temp::new(index); 171 | data.temp_data.push(&mir.temp_decls[temp]); 172 | } 173 | 174 | // Get the variable declarations 175 | for index in 0..mir.var_decls.len() { 176 | let var = Var::new(index); 177 | data.var_data.push(&mir.var_decls[var]); 178 | } 179 | 180 | if debug { 181 | println!("Printing basic blocks..."); 182 | for index in 0..data.block_data.len() { 183 | println!("bb{:?}\n{:#?}\n", index, data.block_data[index]); 184 | } 185 | } 186 | 187 | // Generate the weakest precondition 188 | let weakest_precondition = gen(0, &mut data, &post_expr, debug); 189 | 190 | // Create the verification condition, P -> WP 191 | let verification_condition: Expression = Expression::BinaryExpression( BinaryExpressionData{ 192 | op: BinaryOperator::Implication, 193 | left: Box::new(pre_expr.as_ref().unwrap().clone()), 194 | right: Box::new(weakest_precondition.as_ref().unwrap().clone()) 195 | } ); 196 | 197 | // FIXME: Debug should not be a const; it must be user-facing 198 | if debug { 199 | println!("vc: {}\n", verification_condition); 200 | } 201 | // Check that the verification condition is correctly typed 202 | match expression::ty_check(&verification_condition) { 203 | Ok(_) => {}, 204 | Err(e) => rp_error!("{}", e), 205 | } 206 | 207 | // Output to SMT-LIB format 208 | gen_smtlib(&verification_condition, name, debug); 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/parser/expression_parser.lalrpop: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | use expression::*; 3 | grammar; 4 | 5 | pub E1: Expression = { 6 | => Expression::BinaryExpression( BinaryExpressionData { 7 | op: op, left: Box::new(left), right: Box::new(right) 8 | } ), 9 | E2, 10 | }; 11 | 12 | E2: Expression = { 13 | => Expression::BinaryExpression( BinaryExpressionData { 14 | op: op, left: Box::new(left), right: Box::new(right) 15 | } ), 16 | E3, 17 | }; 18 | 19 | E3: Expression = { 20 | => Expression::BinaryExpression( BinaryExpressionData { 21 | op: op, left: Box::new(left), right: Box::new(right) 22 | } ), 23 | E4, 24 | }; 25 | 26 | E4: Expression = { 27 | => Expression::BinaryExpression( BinaryExpressionData { 28 | op: op, left: Box::new(left), right: Box::new(right) 29 | } ), 30 | E5, 31 | }; 32 | 33 | E5: Expression = { 34 | => Expression::BinaryExpression( BinaryExpressionData { 35 | op: op, left: Box::new(left), right: Box::new(right) 36 | } ), 37 | E6, 38 | }; 39 | 40 | E6: Expression = { 41 | => Expression::BinaryExpression( BinaryExpressionData { 42 | op: op, left: Box::new(left), right: Box::new(right) 43 | } ), 44 | E7, 45 | }; 46 | 47 | E7: Expression = { 48 | => Expression::BinaryExpression( BinaryExpressionData { 49 | op: op, left: Box::new(left), right: Box::new(right) 50 | } ), 51 | E8, 52 | }; 53 | 54 | E8: Expression = { 55 | => Expression::BinaryExpression( BinaryExpressionData { 56 | op: op, left: Box::new(left), right: Box::new(right) 57 | } ), 58 | E9, 59 | }; 60 | 61 | E9: Expression = { 62 | => Expression::UnaryExpression( UnaryExpressionData { 63 | op: op, e: Box::new(e) 64 | } ), 65 | E10, 66 | }; 67 | 68 | E10: Expression = { 69 | "true" => Expression::BooleanLiteral(true), 70 | "false" => Expression::BooleanLiteral(false), 71 | "i8" => Expression::SignedBitVector( SignedBitVectorData { 72 | size: 8, value: i8::from_str(i).unwrap() as i64 73 | } ), 74 | "i16" => Expression::SignedBitVector( SignedBitVectorData { 75 | size: 16, value: i16::from_str(i).unwrap() as i64 76 | } ), 77 | "i32" => Expression::SignedBitVector( SignedBitVectorData { 78 | size: 32, value: i32::from_str(i).unwrap() as i64 79 | } ), 80 | "i64" => Expression::SignedBitVector( SignedBitVectorData { 81 | size: 64, value: i64::from_str(i).unwrap() as i64 82 | } ), 83 | "i8" => Expression::SignedBitVector( SignedBitVectorData { 84 | size: 8, value: i8::from_str(i).unwrap() as i64 85 | } ), 86 | "i16" => Expression::SignedBitVector( SignedBitVectorData { 87 | size: 16, value: i16::from_str(i).unwrap() as i64 88 | } ), 89 | "i32" => Expression::SignedBitVector( SignedBitVectorData { 90 | size: 32, value: i32::from_str(i).unwrap() as i64 91 | } ), 92 | "i64" => Expression::SignedBitVector( SignedBitVectorData { 93 | size: 64, value: i64::from_str(i).unwrap() as i64 94 | } ), 95 | "u8" => Expression::UnsignedBitVector( UnsignedBitVectorData { 96 | size: 8, value: u8::from_str(i).unwrap() as u64 97 | } ), 98 | "u16" => Expression::UnsignedBitVector( UnsignedBitVectorData { 99 | size: 16, value: u16::from_str(i).unwrap() as u64 100 | } ), 101 | "u32" => Expression::UnsignedBitVector( UnsignedBitVectorData { 102 | size: 32, value: u32::from_str(i).unwrap() as u64 103 | } ), 104 | "u64" => Expression::UnsignedBitVector( UnsignedBitVectorData { 105 | size: 64, value: u64::from_str(i).unwrap() as u64 106 | } ), 107 | ":" => Expression::VariableMapping( VariableMappingData { 108 | name: n, var_type: t 109 | } ), 110 | => ib, 111 | "(" ")" => e, 112 | }; 113 | 114 | UOP: UnaryOperator = { 115 | "-" => UnaryOperator::Negation, 116 | "!" => UnaryOperator::BitwiseNot, 117 | "NOT" => UnaryOperator::Not, 118 | }; 119 | 120 | BOP1: BinaryOperator = { 121 | "&&" => BinaryOperator::And, 122 | "AND" => BinaryOperator::And, 123 | "||" => BinaryOperator::Or, 124 | "OR" => BinaryOperator::Or, 125 | "XOR" => BinaryOperator::Xor, 126 | "=>" => BinaryOperator::Implication, 127 | "IMPLIES" => BinaryOperator::Implication, 128 | "<=>" => BinaryOperator::BiImplication, 129 | "EQUIV" => BinaryOperator::BiImplication, 130 | }; 131 | 132 | BOP2: BinaryOperator = { 133 | "<" => BinaryOperator::LessThan, 134 | "<=" => BinaryOperator::LessThanOrEqual, 135 | ">" => BinaryOperator::GreaterThan, 136 | ">=" => BinaryOperator::GreaterThanOrEqual, 137 | "==" => BinaryOperator::Equal, 138 | "!=" => BinaryOperator::NotEqual, 139 | }; 140 | 141 | BOP3: BinaryOperator = { 142 | "|" => BinaryOperator::BitwiseOr, 143 | }; 144 | 145 | BOP4: BinaryOperator = { 146 | "^" => BinaryOperator::BitwiseXor, 147 | }; 148 | 149 | BOP5: BinaryOperator = { 150 | "&" => BinaryOperator::BitwiseAnd, 151 | }; 152 | 153 | BOP6: BinaryOperator = { 154 | "<<" => BinaryOperator::BitwiseLeftShift, 155 | ">>" => BinaryOperator::BitwiseRightShift, 156 | }; 157 | 158 | BOP7: BinaryOperator = { 159 | "+" => BinaryOperator::Addition, 160 | "-" => BinaryOperator::Subtraction, 161 | }; 162 | 163 | BOP8: BinaryOperator = { 164 | "*" => BinaryOperator::Multiplication, 165 | "/" => BinaryOperator::Division, 166 | "%" => BinaryOperator::Modulo, 167 | }; 168 | 169 | IDENTIFIER: String = { 170 | => i.to_string(), 171 | => { 172 | match i { 173 | "as" | "box" | "break" | "const" | "continue" | "crate" | "else" | "enum" | "extern" | 174 | "false" | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "match" | "mod" | 175 | "move" | "mut" | "pub" | "ref" | "self" | "Self" | "static" | "struct" | 176 | "super" | "trait" | "true" | "type" | "unsafe" | "use" | "where" | "while" | 177 | "abstract" | "alignof" | "become" | "do" | "final" | "macro" | "offsetof" | 178 | "override" | "priv" | "proc" | "pure" | "sizeof" | "typeof" | "unsized" | "virtual" | 179 | "yield" => { 180 | panic!("Use of reserved keyword as identifier: {}", i.to_string()); 181 | }, 182 | _ => i.to_string(), 183 | } 184 | }, 185 | }; 186 | 187 | TYPE: Types = { 188 | "bool" => Types::Bool, 189 | "i8" => Types::I8, 190 | "i16" => Types::I16, 191 | "i32" => Types::I32, 192 | "i64" => Types::I64, 193 | "u8" => Types::U8, 194 | "u16" => Types::U16, 195 | "u32" => Types::U32, 196 | "u64" => Types::U64, 197 | }; 198 | 199 | INT_BOUNDS: Expression = { 200 | "i8::MAX" => Expression::SignedBitVector( SignedBitVectorData { 201 | size: 8, value: i8::max_value() as i64 202 | } ), 203 | "i16::MAX" => Expression::SignedBitVector( SignedBitVectorData { 204 | size: 16, value: i16::max_value() as i64 205 | } ), 206 | "i32::MAX" => Expression::SignedBitVector( SignedBitVectorData { 207 | size: 32, value: i32::max_value() as i64 208 | } ), 209 | "i64::MAX" => Expression::SignedBitVector( SignedBitVectorData { 210 | size: 64, value: i64::max_value() as i64 211 | } ), 212 | "i8::MIN" => Expression::SignedBitVector( SignedBitVectorData { 213 | size: 8, value: i8::min_value() as i64 214 | } ), 215 | "i16::MIN" => Expression::SignedBitVector( SignedBitVectorData { 216 | size: 16, value: i16::min_value() as i64 217 | } ), 218 | "i32::MIN" => Expression::SignedBitVector( SignedBitVectorData { 219 | size: 32, value: i32::min_value() as i64 220 | } ), 221 | "i64::MIN" => Expression::SignedBitVector( SignedBitVectorData { 222 | size: 64, value: i64::min_value() as i64 223 | } ), 224 | "u8::MAX" => Expression::UnsignedBitVector( UnsignedBitVectorData { 225 | size: 8, value: u8::max_value() as u64 226 | } ), 227 | "u16::MAX" => Expression::UnsignedBitVector( UnsignedBitVectorData { 228 | size: 16, value: u16::max_value() as u64 229 | } ), 230 | "u32::MAX" => Expression::UnsignedBitVector( UnsignedBitVectorData { 231 | size: 32, value: u32::max_value() as u64 232 | } ), 233 | "u64::MAX" => Expression::UnsignedBitVector( UnsignedBitVectorData { 234 | size: 64, value: u64::max_value() as u64 235 | } ), 236 | "u8::MIN" => Expression::UnsignedBitVector( UnsignedBitVectorData { 237 | size: 8, value: u8::min_value() as u64 238 | } ), 239 | "u16::MIN" => Expression::UnsignedBitVector( UnsignedBitVectorData { 240 | size: 16, value: u16::min_value() as u64 241 | } ), 242 | "u32::MIN" => Expression::UnsignedBitVector( UnsignedBitVectorData { 243 | size: 32, value: u32::min_value() as u64 244 | } ), 245 | "u64::MIN" => Expression::UnsignedBitVector( UnsignedBitVectorData { 246 | size: 64, value: u64::min_value() as u64 247 | } ), 248 | }; 249 | -------------------------------------------------------------------------------- /src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Parses rustproof attribute definitions. 12 | 13 | extern crate syntax; 14 | 15 | mod expression_parser; 16 | 17 | use syntax::ast::{MetaItemKind, Attribute_}; 18 | use syntax::codemap::Spanned; 19 | use expression::{Expression, ty_check}; 20 | 21 | /// Analyzes an attribute on a function in the compiled code, and if the attribute is "condition", 22 | /// ensures correct usage. If usage is correct, it stores the argument strings. 23 | /// 24 | /// # Arguments: 25 | /// * `pre_string` - Empty string. Will contain a user-submitted precondition if found. 26 | /// * `post_string` - Empty string. Will contain a user-submitted postcondition if found. 27 | /// * `attr` - The attribute being analyzed. 28 | /// 29 | /// # Remarks: 30 | /// * Currently supported `ConstInt`: `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64`, `bool` 31 | /// 32 | pub fn parse_attribute(pre_string: &mut String, 33 | post_string: &mut String, 34 | attr: &Spanned) { 35 | if let MetaItemKind::List(ref attribute_name, ref args) = attr.node.value.node { 36 | // Ignore if not a condition attribute 37 | if attribute_name == "condition" { 38 | // Only accept if exactly 2 arguments 39 | if args.len() != 2 { 40 | rp_error!("Condition attribute must have exactly 2 arguments."); 41 | } 42 | // Parse the first argument 43 | if let MetaItemKind::NameValue(ref i_string, ref literal) = args[0].node { 44 | if i_string != "pre" { 45 | rp_error!( "The first argument must be named \"pre\". {} was provided.", 46 | i_string); 47 | } 48 | // Get the argument 49 | if let syntax::ast::LitKind::Str(ref i_string, _) = literal.node { 50 | *pre_string = i_string.to_string(); 51 | } else { 52 | rp_error!("Conditions must be strings. \ 53 | Try wrapping conditions in quotation marks."); 54 | } 55 | } else { 56 | rp_error!("The first argument must be named \"pre\"."); 57 | } 58 | // Parse the second argument 59 | if let MetaItemKind::NameValue(ref i_string, ref literal) = args[1].node { 60 | if i_string != "post" { 61 | rp_error!( "The second argument must be named \"post\". {} was provided.", 62 | i_string); 63 | } 64 | // Get the argument 65 | if let syntax::ast::LitKind::Str(ref i_string, _) = literal.node { 66 | *post_string = i_string.to_string(); 67 | } else { 68 | rp_error!("Conditions must be strings. \ 69 | Try wrapping conditions in quotation marks."); 70 | } 71 | } else { 72 | rp_error!("The second argument must be named \"post\"."); 73 | } 74 | } // Ignore if not a condition attribute 75 | } 76 | } 77 | 78 | /// Calls the expression parser on a given precondition or postcondition. 79 | /// 80 | /// # Arguments: 81 | /// * `condition` - A user-submitted string 82 | /// 83 | /// # Return: 84 | /// * If `condition` is valid, an Expression representing it. 85 | /// 86 | /// # Remarks: 87 | /// * Current supported types: `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64`, `bool` 88 | pub fn parse_condition(condition: &str) -> Expression { 89 | match expression_parser::parse_E1(condition) { 90 | Ok(e) => { 91 | match ty_check(&e) { 92 | Ok(_) => return e, 93 | Err(s) => rp_error!("{}", s), 94 | } 95 | }, 96 | Err(e) => rp_error!("Error parsing condition \"{}\": {:?}", condition, e) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/reporting/mod.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Prints user-facing errors and warnings. 12 | 13 | // Warning macro 14 | macro_rules! rp_warn { 15 | ($fmt:expr) => ({ 16 | use rustc_errors::{ColorConfig, Handler}; 17 | use syntax::codemap::CodeMap; 18 | use std::rc::Rc; 19 | let codemap = Rc::new(CodeMap::new()); 20 | let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone())); 21 | let str = format!(concat!($fmt, "\n")); 22 | handler.warn(&str); 23 | }); 24 | ($fmt:expr, $($arg:tt)*) => ({ 25 | use rustc_errors::{ColorConfig, Handler}; 26 | use syntax::codemap::CodeMap; 27 | use std::rc::Rc; 28 | let codemap = Rc::new(CodeMap::new()); 29 | let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone())); 30 | let str = format!(concat!($fmt, "\n"), $($arg)*); 31 | handler.warn(&str); 32 | }); 33 | } 34 | 35 | // Error macro 36 | macro_rules! rp_error { 37 | ($fmt:expr) => ({ 38 | use rustc_errors::{ColorConfig, Handler}; 39 | use syntax::codemap::CodeMap; 40 | use std::rc::Rc; 41 | use std::process; 42 | let codemap = Rc::new(CodeMap::new()); 43 | let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone())); 44 | let str = concat!($fmt, "\n"); 45 | handler.err(&str); 46 | process::exit(1); 47 | }); 48 | ($fmt:expr, $($arg:tt)*) => ({ 49 | use rustc_errors::{ColorConfig, Handler}; 50 | use syntax::codemap::CodeMap; 51 | use std::rc::Rc; 52 | use std::process; 53 | let codemap = Rc::new(CodeMap::new()); 54 | let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone())); 55 | let str = format!(concat!($fmt, "\n"), $($arg)*); 56 | handler.err(&str); 57 | process::exit(1); 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /src/smt_output/mod.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Interface between rustproof and libsmt(z3). 12 | 13 | use std::fmt::Debug; 14 | 15 | use rustproof_libsmt::backends::smtlib2::*; 16 | use rustproof_libsmt::backends::backend::*; 17 | use rustproof_libsmt::backends::z3; 18 | use rustproof_libsmt::theories::{bitvec, core}; 19 | use rustproof_libsmt::logics::qf_abv::*; 20 | use petgraph::graph::NodeIndex; 21 | 22 | use expression::*; 23 | 24 | /// Invokes Z3 to check the satisfiability of a verification condition. 25 | /// 26 | /// # Arguments: 27 | /// * `vc` - A verification condition as an Expression. 28 | /// * `name` - The name of the function whose verification condition is being checked. 29 | /// * `debug` - A flag to enable/disable debug printing. 30 | /// 31 | /// # Remarks: 32 | /// * Simply satisfying P->WP isn't enough; that will only tell us if P->WP is _sometimes true_. We 33 | /// * need to verify that !(P->WP) is *unsatisfiable* to determine that P->WP is _always true_. 34 | /// 35 | pub fn gen_smtlib (vc: &Expression, name: String, debug: bool) { 36 | // Define an instance of Z3 37 | let mut z3: z3::Z3 = Default::default(); 38 | 39 | // Declare a logic to use 40 | let mut solver = SMTLib2::new(Some(QF_ABV)); 41 | 42 | // Check the satisfiability of the solver 43 | let vcon = solver.expr2smtlib(vc); 44 | let _ = solver.assert(core::OpCodes::Not, &[vcon]); 45 | 46 | let (_, check) = solver.solve(&mut z3, debug); 47 | match check { 48 | SMTRes::Sat(_, ref model) => { 49 | println!( 50 | "\nfn {}(..)\tVerification Condition is not valid.\n\n{}\n", 51 | name, 52 | model.clone().unwrap() 53 | ); 54 | }, 55 | SMTRes::Unsat(..) => { 56 | println!("\nfn {}(..)\tVerification Condition is valid.\n", name); 57 | }, 58 | SMTRes::Error(ref error, _) => { 59 | println!("\nfn {}(..)\tError in Verification Condition Generation.\n{}\n", name, error); 60 | } 61 | } 62 | 63 | } 64 | 65 | pub trait Pred2SMT { 66 | type Idx: Debug + Clone; 67 | type Logic: Logic; 68 | 69 | fn expr2smtlib (&mut self, &Expression) -> Self::Idx; 70 | } 71 | 72 | impl Pred2SMT for SMTLib2 { 73 | type Idx = NodeIndex; 74 | type Logic = QF_ABV; 75 | 76 | fn expr2smtlib (&mut self, vc: &Expression) -> Self::Idx { 77 | match *vc { 78 | Expression::BinaryExpression (ref b) => { 79 | let l = self.expr2smtlib(b.left.as_ref()); 80 | let r = self.expr2smtlib(b.right.as_ref()); 81 | match b.op { 82 | BinaryOperator::Addition => { 83 | return self.assert(bitvec::OpCodes::BvAdd, &[l,r]); 84 | }, 85 | BinaryOperator::Subtraction => { 86 | return self.assert(bitvec::OpCodes::BvSub, &[l,r]); 87 | }, 88 | BinaryOperator::Multiplication => { 89 | return self.assert(bitvec::OpCodes::BvMul, &[l,r]); 90 | }, 91 | BinaryOperator::Division => { 92 | // Check for signedness 93 | if is_signed_type(determine_evaluation_type(vc)) { 94 | return self.assert(bitvec::OpCodes::BvSDiv, &[l,r]); 95 | } else { 96 | return self.assert(bitvec::OpCodes::BvUDiv, &[l,r]); 97 | } 98 | }, 99 | BinaryOperator::Modulo => { 100 | // Check for signedness 101 | if is_signed_type(determine_evaluation_type(vc)) { 102 | return self.assert(bitvec::OpCodes::BvSMod, &[l,r]); 103 | } else { 104 | return self.assert(bitvec::OpCodes::BvURem, &[l,r]); 105 | } 106 | }, 107 | BinaryOperator::SignedMultiplicationDoesNotOverflow => { 108 | return self.assert(bitvec::OpCodes::BvSMulDoesNotOverflow, &[l,r]); 109 | }, 110 | BinaryOperator::SignedMultiplicationDoesNotUnderflow => { 111 | return self.assert(bitvec::OpCodes::BvSMulDoesNotUnderflow, &[l,r]); 112 | }, 113 | BinaryOperator::UnsignedMultiplicationDoesNotOverflow => { 114 | return self.assert(bitvec::OpCodes::BvUMulDoesNotOverflow, &[l,r]); 115 | }, 116 | BinaryOperator::BitwiseOr => { 117 | if determine_evaluation_type(vc) == Types::Bool { 118 | return self.assert(core::OpCodes::Or, &[l,r]); 119 | } else { 120 | return self.assert(bitvec::OpCodes::BvOr, &[l,r]); 121 | } 122 | }, 123 | BinaryOperator::BitwiseAnd => { 124 | if determine_evaluation_type(vc) == Types::Bool { 125 | return self.assert(core::OpCodes::And, &[l,r]); 126 | } else { 127 | return self.assert(bitvec::OpCodes::BvAnd, &[l,r]); 128 | } 129 | }, 130 | BinaryOperator::BitwiseXor => { 131 | if determine_evaluation_type(vc) == Types::Bool { 132 | return self.assert(core::OpCodes::Xor, &[l,r]); 133 | } else { 134 | return self.assert(bitvec::OpCodes::BvXor, &[l,r]); 135 | } 136 | }, 137 | BinaryOperator::BitwiseLeftShift => { 138 | return self.assert(bitvec::OpCodes::BvShl, &[l,r]); 139 | }, 140 | BinaryOperator::BitwiseRightShift => { 141 | // Check for signedness 142 | if is_signed_type(determine_evaluation_type(vc)) { 143 | return self.assert(bitvec::OpCodes::BvAShr, &[l,r]); 144 | } else { 145 | return self.assert(bitvec::OpCodes::BvLShr, &[l,r]); 146 | } 147 | }, 148 | BinaryOperator::LessThan => { 149 | if is_signed_type(determine_evaluation_type(b.left.as_ref())) { 150 | return self.assert(bitvec::OpCodes::BvSLt, &[l,r]); 151 | } else { 152 | return self.assert(bitvec::OpCodes::BvULt, &[l,r]); 153 | } 154 | }, 155 | BinaryOperator::LessThanOrEqual => { 156 | // Check for signedness 157 | if is_signed_type(determine_evaluation_type(b.left.as_ref())) { 158 | return self.assert(bitvec::OpCodes::BvSLe, &[l,r]); 159 | } else { 160 | return self.assert(bitvec::OpCodes::BvULe, &[l,r]); 161 | } 162 | }, 163 | BinaryOperator::GreaterThan => { 164 | // Check for signedness 165 | if is_signed_type(determine_evaluation_type(b.left.as_ref())) { 166 | return self.assert(bitvec::OpCodes::BvSGt, &[l,r]); 167 | } else { 168 | return self.assert(bitvec::OpCodes::BvUGt, &[l,r]); 169 | } 170 | }, 171 | BinaryOperator::GreaterThanOrEqual => { 172 | // Check for signedness 173 | if is_signed_type(determine_evaluation_type(b.left.as_ref())) { 174 | return self.assert(bitvec::OpCodes::BvSGe, &[l,r]); 175 | } else { 176 | return self.assert(bitvec::OpCodes::BvUGe, &[l,r]); 177 | } 178 | }, 179 | BinaryOperator::Equal 180 | | BinaryOperator::BiImplication => { 181 | return self.assert(core::OpCodes::Cmp, &[l,r]); 182 | } 183 | BinaryOperator::NotEqual => { 184 | let eq = self.assert(core::OpCodes::Cmp, &[l,r]); 185 | return self.assert(core::OpCodes::Not, &[eq]); 186 | }, 187 | BinaryOperator::And => { 188 | return self.assert(core::OpCodes::And, &[l,r]); 189 | }, 190 | BinaryOperator::Or => { 191 | return self.assert(core::OpCodes::Or, &[l,r]); 192 | }, 193 | BinaryOperator::Xor => { 194 | return self.assert(core::OpCodes::Xor, &[l,r]); 195 | }, 196 | BinaryOperator::Implication => { 197 | return self.assert(core::OpCodes::Imply, &[l,r]); 198 | }, 199 | } 200 | }, 201 | Expression::UnaryExpression (ref u) => { 202 | let n = self.expr2smtlib(u.e.as_ref()); 203 | match u.op { 204 | UnaryOperator::Negation => { 205 | return self.assert(bitvec::OpCodes::BvNeg, &[n]); 206 | }, 207 | UnaryOperator::BitwiseNot => { 208 | return self.assert(bitvec::OpCodes::BvNot, &[n]); 209 | }, 210 | UnaryOperator::Not => { 211 | return self.assert(core::OpCodes::Not, &[n]); 212 | }, 213 | } 214 | }, 215 | Expression::VariableMapping (ref v) => { 216 | let sort = match v.var_type { 217 | Types::Bool => bitvec::Sorts::Bool, 218 | Types::I8 | Types::U8 => bitvec::Sorts::BitVector(8), 219 | Types::I16 | Types::U16 => bitvec::Sorts::BitVector(16), 220 | Types::I32 | Types::U32 => bitvec::Sorts::BitVector(32), 221 | Types::I64 | Types::U64 => bitvec::Sorts::BitVector(64), 222 | Types::Void => unreachable!(), 223 | }; 224 | return self.new_var(Some(&v.name), sort); 225 | }, 226 | Expression::BooleanLiteral (ref b) => { 227 | return self.new_const(core::OpCodes::Const(*b)); 228 | }, 229 | Expression::UnsignedBitVector (ref u) => { 230 | return bv_const!(self, u.value, u.size as usize); 231 | }, 232 | Expression::SignedBitVector (ref s) => { 233 | return bv_const!(self, s.value as u64, s.size as usize); 234 | } 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | mod test_reporting; 12 | mod test_expression; 13 | mod system_tests; 14 | -------------------------------------------------------------------------------- /src/tests/system_tests.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use std::process::Command; 12 | 13 | // Uses a /example file as a system test for rustproof 14 | // returns false on verification condition mismatch from function name prefix 15 | fn test_example_file(file: &str) -> bool { 16 | 17 | // Clean rustproof to ensure this test runs 18 | // Note: this does not increase test time 19 | Command::new("cargo").args(&["clean","-p", "rustproof"]).output() 20 | .expect("failed to execute child process: cargo clean -p rustproof"); 21 | 22 | // Compile the example 23 | let output = Command::new("cargo").args(&["build", "--test", file]).output() 24 | .expect(format!("failed to execute child process: cargo build --test {}", file).as_str()); 25 | 26 | // Read strerr for any rustproof errors. if found, return false 27 | let stderr_result = String::from_utf8_lossy(&output.stderr); 28 | let split_err = stderr_result.split("\n"); 29 | for s in split_err { 30 | if s.starts_with("error") { 31 | return false; 32 | } 33 | } 34 | 35 | // Process the output 36 | let stdout_result = String::from_utf8_lossy(&output.stdout); 37 | let split = stdout_result.split("\n"); 38 | 39 | // For each function 40 | for s in split{ 41 | if s.starts_with("fn") { 42 | // If the output line starts with "invalid" it must end with "not valid" 43 | // If the output line starts with "valid" it must end with "valid" 44 | // If there is a mismatch, we have a test failure. 45 | // Lines beginning with anything else should be ignored 46 | if !((s.starts_with("fn invalid") && s.ends_with("not valid.")) 47 | || (s.starts_with("fn valid") && s.ends_with("valid.") && !s.ends_with("not valid."))) { 48 | return false; 49 | } 50 | } 51 | } 52 | 53 | return true; 54 | } 55 | 56 | // Original test condition example test 57 | #[test] 58 | fn test_examples() { 59 | assert!(test_example_file("test_conditions")); 60 | } 61 | 62 | // Test examples for unsigned examples 63 | #[test] 64 | fn test_unsigned_examples(){ 65 | assert!(test_example_file("test_unsigned")); 66 | } 67 | 68 | // Test examples for signed examples 69 | #[test] 70 | fn test_signed_examples(){ 71 | assert!(test_example_file("test_signed")); 72 | } 73 | 74 | // Test example for boolean examples 75 | #[test] 76 | fn test_boolean_examples(){ 77 | assert!(test_example_file("test_boolean_arithmetic")); 78 | } 79 | 80 | // Test example for conditional examples 81 | #[test] 82 | fn test_conditional_examples(){ 83 | assert!(test_example_file("test_conditionals")); 84 | } 85 | 86 | // Test example for assertion examples 87 | #[test] 88 | fn test_assertion_examples(){ 89 | assert!(test_example_file("test_assert")); 90 | } 91 | 92 | // Tests whether the system testing functions are appropriately catching test failure 93 | #[test] 94 | #[should_panic] 95 | fn test_system_test_validity() { 96 | assert!(test_example_file("test_fail_valid")); 97 | assert!(test_example_file("test_fail_invalid")); 98 | } 99 | -------------------------------------------------------------------------------- /src/tests/test_expression.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use expression::*; 12 | 13 | #[test] 14 | fn test_all_substitute_binary_operators() { 15 | substitute_variable_with_expression_less_than(); 16 | substitute_variable_with_expression_less_than_or_equal(); 17 | substitute_variable_with_expression_greater_than(); 18 | substitute_variable_with_expression_greater_than_or_equal(); 19 | } 20 | 21 | #[test] 22 | fn test_check_signedness() { 23 | check_signedness_bool(); 24 | check_signedness_i8(); 25 | check_signedness_i16(); 26 | check_signedness_i32(); 27 | check_signedness_i64(); 28 | check_signedness_u8(); 29 | check_signedness_u16(); 30 | check_signedness_u32(); 31 | check_signedness_u64(); 32 | } 33 | 34 | #[test] 35 | fn determine_evaluation_type_comparison_unary(){ 36 | let u: Expression = Expression::VariableMapping( VariableMappingData{ 37 | name: "x".to_string(), 38 | var_type: Types::Bool 39 | }); 40 | let to_test: Expression = Expression::UnaryExpression( UnaryExpressionData{ 41 | op: UnaryOperator::Not, 42 | e: Box::new(u.clone()), 43 | }); 44 | let determined_type = determine_evaluation_type(&to_test); 45 | let correct_result = Types::Bool; 46 | assert_eq!(determined_type, correct_result); 47 | } 48 | 49 | #[test] 50 | fn determine_evaluation_type_comparison_binary(){ 51 | let left_side: Expression = Expression::VariableMapping( VariableMappingData{ 52 | name:"x".to_string(), 53 | var_type: Types::I32 54 | }); 55 | let right_side: Expression = Expression::VariableMapping( VariableMappingData{ 56 | name:"y".to_string(), 57 | var_type: Types::I32 58 | }); 59 | let to_test: Expression = Expression::BinaryExpression(BinaryExpressionData{ 60 | op: BinaryOperator::Addition, 61 | left: Box::new(left_side.clone()), 62 | right: Box::new(right_side.clone()), 63 | }); 64 | 65 | let returned_string = determine_evaluation_type(&to_test); 66 | let correct_result = Types::I32; 67 | assert_eq!(returned_string, correct_result); 68 | } 69 | 70 | #[test] 71 | fn substitute_unary_operator_not(){ 72 | let target_var : VariableMappingData = VariableMappingData { 73 | name: "x".to_string(), 74 | var_type: Types::I32 75 | }; 76 | let target: Expression = Expression::VariableMapping(target_var.clone()); 77 | let replacement: Expression = Expression::VariableMapping( VariableMappingData{ 78 | name: "y".to_string(), 79 | var_type: Types::I32 80 | }); 81 | let mut p: Expression = Expression::UnaryExpression(UnaryExpressionData{ 82 | op: UnaryOperator::Not, 83 | e: Box::new(target.clone()), 84 | }); 85 | 86 | let correct_result: Expression = Expression::UnaryExpression(UnaryExpressionData{ 87 | op: UnaryOperator::Not, 88 | e: Box::new(replacement.clone() ), 89 | }); 90 | substitute_variable_with_expression(&mut p, &target_var, &replacement); 91 | assert_eq!(p, correct_result); 92 | } 93 | 94 | fn substitute_variable_with_expression_greater_than_or_equal(){ 95 | let target_var : VariableMappingData = VariableMappingData { 96 | name: "x".to_string(), 97 | var_type: Types::I32 98 | }; 99 | let target: Expression = Expression::VariableMapping( target_var.clone() ); 100 | let superfluous: Expression = Expression::VariableMapping( VariableMappingData { 101 | name: "z".to_string(), 102 | var_type: Types::I32 103 | }); 104 | let replacement: Expression = Expression::VariableMapping( VariableMappingData { 105 | name: "y".to_string(), 106 | var_type: Types::I32 107 | }); 108 | let mut p: Expression = Expression::BinaryExpression( BinaryExpressionData{ 109 | op: BinaryOperator::GreaterThanOrEqual, 110 | left: Box::new( target.clone() ), 111 | right: Box::new( superfluous.clone() ), 112 | }); 113 | 114 | let correct_result: Expression = Expression::BinaryExpression( BinaryExpressionData{ 115 | op: BinaryOperator::GreaterThanOrEqual, 116 | left: Box::new( replacement.clone() ), 117 | right: Box::new( superfluous.clone() ), 118 | }); 119 | substitute_variable_with_expression(&mut p, &target_var, &replacement); 120 | assert_eq!(p, correct_result); 121 | } 122 | 123 | fn substitute_variable_with_expression_less_than() { 124 | let target_var : VariableMappingData = VariableMappingData { 125 | name: "x".to_string(), 126 | var_type: Types::I32 127 | }; 128 | let target: Expression = Expression::VariableMapping( target_var.clone() ); 129 | let superfluous: Expression = Expression::VariableMapping( VariableMappingData { 130 | name: "z".to_string(), 131 | var_type: Types::I32 132 | }); 133 | let replacement: Expression = Expression::VariableMapping( VariableMappingData { 134 | name: "y".to_string(), 135 | var_type: Types::I32 136 | }); 137 | let mut p: Expression = Expression::BinaryExpression( BinaryExpressionData{ 138 | op: BinaryOperator::LessThan, 139 | left: Box::new( target.clone() ), 140 | right: Box::new( superfluous.clone() ), 141 | }); 142 | 143 | let correct_result: Expression = Expression::BinaryExpression( BinaryExpressionData{ 144 | op: BinaryOperator::LessThan, 145 | left: Box::new( replacement.clone() ), 146 | right: Box::new( superfluous.clone() ), 147 | }); 148 | substitute_variable_with_expression(&mut p, &target_var, &replacement); 149 | assert_eq!(p, correct_result); 150 | } 151 | 152 | fn substitute_variable_with_expression_less_than_or_equal() { 153 | let target_var : VariableMappingData = VariableMappingData { 154 | name: "x".to_string(), 155 | var_type: Types::I32 156 | }; 157 | let target: Expression = Expression::VariableMapping( target_var.clone() ); 158 | let superfluous: Expression = Expression::VariableMapping( VariableMappingData { 159 | name: "z".to_string(), 160 | var_type: Types::I32 161 | }); 162 | let replacement: Expression = Expression::VariableMapping( VariableMappingData { 163 | name: "y".to_string(), 164 | var_type: Types::I32 165 | }); 166 | let mut p: Expression = Expression::BinaryExpression( BinaryExpressionData{ 167 | op: BinaryOperator::LessThanOrEqual, 168 | left: Box::new( target.clone() ), 169 | right: Box::new( superfluous.clone() ), 170 | }); 171 | 172 | let correct_result: Expression = Expression::BinaryExpression( BinaryExpressionData{ 173 | op: BinaryOperator::LessThanOrEqual, 174 | left: Box::new( replacement.clone() ), 175 | right: Box::new( superfluous.clone() ), 176 | }); 177 | substitute_variable_with_expression(&mut p, &target_var, &replacement); 178 | assert_eq!(p, correct_result); 179 | } 180 | 181 | fn substitute_variable_with_expression_greater_than(){ 182 | let target_var : VariableMappingData = VariableMappingData { 183 | name: "x".to_string(), 184 | var_type: Types::I32 185 | }; 186 | let target: Expression = Expression::VariableMapping( target_var.clone() ); 187 | let superfluous: Expression = Expression::VariableMapping( VariableMappingData { 188 | name: "z".to_string(), 189 | var_type: Types::I32 190 | }); 191 | let replacement: Expression = Expression::VariableMapping( VariableMappingData { 192 | name: "y".to_string(), 193 | var_type: Types::I32 194 | }); 195 | let mut p: Expression = Expression::BinaryExpression( BinaryExpressionData{ 196 | op: BinaryOperator::GreaterThan, 197 | left: Box::new( target.clone() ), 198 | right: Box::new( superfluous.clone() ), 199 | }); 200 | 201 | let correct_result: Expression = Expression::BinaryExpression( BinaryExpressionData{ 202 | op: BinaryOperator::GreaterThan, 203 | left: Box::new( replacement.clone() ), 204 | right: Box::new( superfluous.clone() ), 205 | }); 206 | substitute_variable_with_expression(&mut p, &target_var, &replacement); 207 | assert_eq!(p, correct_result); 208 | 209 | } 210 | 211 | #[test] 212 | fn variable_mapping_data_equality() { 213 | let var1: VariableMappingData = VariableMappingData { 214 | name: "x".to_string(), 215 | var_type: Types::I32 216 | }; 217 | let var2: VariableMappingData = VariableMappingData { 218 | name: "x".to_string(), 219 | var_type: Types::I32 220 | }; 221 | let var3: VariableMappingData = VariableMappingData { 222 | name: "y".to_string(), 223 | var_type: Types::I32 224 | }; 225 | let var4: VariableMappingData = VariableMappingData { 226 | name: "x".to_string(), 227 | var_type: Types::U32 228 | }; 229 | let var5: VariableMappingData = VariableMappingData { 230 | name: "y".to_string(), 231 | var_type: Types::U32 232 | }; 233 | 234 | assert!(var1 == var1); 235 | assert!(var1 == var2); 236 | assert!(var1 != var3); 237 | assert!(var1 != var4); 238 | assert!(var1 != var5); 239 | } 240 | 241 | fn check_signedness_bool() { 242 | let var: Expression = Expression::VariableMapping(VariableMappingData { 243 | name: "v".to_string(), 244 | var_type: Types::Bool, 245 | }); 246 | let boolean: Expression = Expression::BooleanLiteral(true); 247 | 248 | assert!(!is_valid_signed(&var)); 249 | assert!(!is_valid_unsigned(&var)); 250 | assert!(!is_valid_signed(&boolean)); 251 | assert!(!is_valid_unsigned(&boolean)); 252 | } 253 | 254 | fn check_signedness_i8() { 255 | let var: Expression = Expression::VariableMapping(VariableMappingData { 256 | name: "v".to_string(), 257 | var_type: Types::I8, 258 | }); 259 | let num: Expression = Expression::SignedBitVector(SignedBitVectorData { 260 | size: 8u8, 261 | value: 1i64, 262 | }); 263 | 264 | assert!(is_valid_signed(&var)); 265 | assert!(is_valid_signed(&num)); 266 | assert!(!is_valid_unsigned(&var)); 267 | assert!(!is_valid_unsigned(&num)); 268 | } 269 | 270 | fn check_signedness_i16() { 271 | let var: Expression = Expression::VariableMapping(VariableMappingData { 272 | name: "v".to_string(), 273 | var_type: Types::I16, 274 | }); 275 | let num: Expression = Expression::SignedBitVector(SignedBitVectorData { 276 | size: 16u8, 277 | value: 1i64, 278 | }); 279 | 280 | assert!(is_valid_signed(&var)); 281 | assert!(is_valid_signed(&num)); 282 | assert!(!is_valid_unsigned(&var)); 283 | assert!(!is_valid_unsigned(&num)); 284 | } 285 | 286 | fn check_signedness_i32() { 287 | let var: Expression = Expression::VariableMapping(VariableMappingData { 288 | name: "v".to_string(), 289 | var_type: Types::I32, 290 | }); 291 | let num: Expression = Expression::SignedBitVector(SignedBitVectorData { 292 | size: 32u8, 293 | value: 1i64, 294 | }); 295 | 296 | assert!(is_valid_signed(&var)); 297 | assert!(is_valid_signed(&num)); 298 | assert!(!is_valid_unsigned(&var)); 299 | assert!(!is_valid_unsigned(&num)); 300 | } 301 | 302 | fn check_signedness_i64() { 303 | let var: Expression = Expression::VariableMapping(VariableMappingData { 304 | name: "v".to_string(), 305 | var_type: Types::I64, 306 | }); 307 | let num: Expression = Expression::SignedBitVector(SignedBitVectorData { 308 | size: 64u8, 309 | value: 1i64, 310 | }); 311 | 312 | assert!(is_valid_signed(&var)); 313 | assert!(is_valid_signed(&num)); 314 | assert!(!is_valid_unsigned(&var)); 315 | assert!(!is_valid_unsigned(&num)); 316 | } 317 | 318 | fn check_signedness_u8() { 319 | let var: Expression = Expression::VariableMapping(VariableMappingData { 320 | name: "v".to_string(), 321 | var_type: Types::U8, 322 | }); 323 | let num: Expression = Expression::UnsignedBitVector(UnsignedBitVectorData { 324 | size: 8u8, 325 | value: 1u64, 326 | }); 327 | 328 | assert!(!is_valid_signed(&var)); 329 | assert!(!is_valid_signed(&num)); 330 | assert!(is_valid_unsigned(&var)); 331 | assert!(is_valid_unsigned(&num)); 332 | } 333 | 334 | fn check_signedness_u16() { 335 | let var: Expression = Expression::VariableMapping(VariableMappingData { 336 | name: "v".to_string(), 337 | var_type: Types::U16, 338 | }); 339 | let num: Expression = Expression::UnsignedBitVector(UnsignedBitVectorData { 340 | size: 16u8, 341 | value: 1u64, 342 | }); 343 | 344 | assert!(!is_valid_signed(&var)); 345 | assert!(!is_valid_signed(&num)); 346 | assert!(is_valid_unsigned(&var)); 347 | assert!(is_valid_unsigned(&num)); 348 | } 349 | 350 | fn check_signedness_u32() { 351 | let var: Expression = Expression::VariableMapping(VariableMappingData { 352 | name: "v".to_string(), 353 | var_type: Types::U32, 354 | }); 355 | let num: Expression = Expression::UnsignedBitVector(UnsignedBitVectorData { 356 | size: 32u8, 357 | value: 1u64, 358 | }); 359 | 360 | assert!(!is_valid_signed(&var)); 361 | assert!(!is_valid_signed(&num)); 362 | assert!(is_valid_unsigned(&var)); 363 | assert!(is_valid_unsigned(&num)); 364 | } 365 | 366 | fn check_signedness_u64() { 367 | let var: Expression = Expression::VariableMapping(VariableMappingData { 368 | name: "v".to_string(), 369 | var_type: Types::U64, 370 | }); 371 | let num: Expression = Expression::UnsignedBitVector(UnsignedBitVectorData { 372 | size: 64u8, 373 | value: 1u64, 374 | }); 375 | 376 | assert!(!is_valid_signed(&var)); 377 | assert!(!is_valid_signed(&num)); 378 | assert!(is_valid_unsigned(&var)); 379 | assert!(is_valid_unsigned(&num)); 380 | } -------------------------------------------------------------------------------- /src/tests/test_reporting.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | -------------------------------------------------------------------------------- /src/weakest_precondition/mod.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Generates a weakest precondition from MIR basic block data. 12 | 13 | extern crate rustc_const_math; 14 | 15 | use super::MirData; 16 | use expression::*; 17 | use rustc::mir::repr::*; 18 | use rustc::middle::const_val::ConstVal; 19 | use rustc_const_math::ConstInt; 20 | use rustc_data_structures::indexed_vec::Idx; 21 | use rustc::ty::{TypeVariants}; 22 | 23 | mod overflow; 24 | 25 | /// Computes the weakest precondition for a given postcondition and a series of statements over one or more MIR basic blocks. 26 | /// 27 | /// # Arguments: 28 | /// * `index` - The index of the `BasicBlock` within MIR. 29 | /// * `data` - Contains the `BasicBlockData` and all argument, temp, and variable declarations from the MIR pass. 30 | /// * `post_expr` - The current weakest precondition (originally the postcondition) as an Expression. 31 | /// 32 | /// # Return Value: 33 | /// * Returns the weakest precondition generated from the `BasicBlock` in the form of an Expression. 34 | /// 35 | /// # Remarks: 36 | /// * This is the main generator for the weakest precondition, which evaluates the `BasicBlock`s recursively. 37 | /// 38 | pub fn gen(index: usize, data: &mut MirData, post_expr: &Option, debug: bool) -> Option { 39 | let mut wp: Option; 40 | 41 | // Parse basic block terminator data 42 | let terminator = data.block_data[index].terminator.clone().unwrap().kind; 43 | match terminator { 44 | // Assert{cond, expected, msg, target, cleanup} 45 | TerminatorKind::Assert{target, ..} 46 | | TerminatorKind::Goto{target} => { 47 | // Retrieve the weakest precondition from the following block 48 | wp = gen(target.index(), data, post_expr, debug); 49 | }, 50 | TerminatorKind::Return => { 51 | // Return the post condition to the preceeding block 52 | return post_expr.clone(); 53 | }, 54 | // Call{func, args, destination, cleanup} 55 | TerminatorKind::Call{func, ..} => { 56 | // Determine if this is the end of a panic. (assumed false branch of assertion, so 57 | // return a precondition of false [this path will never be taken]) 58 | match func { 59 | Operand::Constant (ref c) => { 60 | let s = format!("{:?}", c.literal); 61 | if s.contains("begin_panic") { 62 | return Some(Expression::BooleanLiteral(false)); 63 | } 64 | }, 65 | // Consume (ref l) 66 | Operand::Consume (..) => unimplemented!(), 67 | }; 68 | // Due to the limited nature in which we handle Calls, we should never do anything 69 | // other than return early or hit the unimplemented!() panic above. 70 | unreachable!(); 71 | }, 72 | // Conditional statements 73 | // wp(if c x else y) => (c -> x) AND ((NOT c) -> y) 74 | TerminatorKind::If{cond, targets} => { 75 | // Generate weakest precondition for if and else clause 76 | let wp_if = gen(targets.0.index(), data, post_expr, debug); 77 | let wp_else = gen(targets.1.index(), data, post_expr, debug); 78 | 79 | // Generate the conditional expression 80 | let condition = match cond { 81 | Operand::Constant (ref constant) => { 82 | match constant.literal { 83 | Literal::Value {ref value} => { 84 | match *value { 85 | ConstVal::Bool (ref boolean) => { 86 | Expression::BooleanLiteral(*boolean) 87 | }, 88 | _ => unreachable!(), 89 | } 90 | }, 91 | _ => unimplemented!(), 92 | } 93 | }, 94 | Operand::Consume(c) => { Expression::VariableMapping(gen_lvalue(c, data)) }, 95 | }; 96 | // Negate the conditional expression 97 | let not_condition = Expression::UnaryExpression(UnaryExpressionData { 98 | op: UnaryOperator::Not, 99 | e: Box::new(condition.clone()) 100 | }); 101 | // wp(If c x else y) => (c -> x) AND ((NOT c) -> y) 102 | wp = Some(Expression::BinaryExpression(BinaryExpressionData { 103 | op: BinaryOperator::And, 104 | left: Box::new(Expression::BinaryExpression(BinaryExpressionData { 105 | op: BinaryOperator::Implication, 106 | left: Box::new(condition.clone()), 107 | right: Box::new(wp_if.unwrap()) 108 | })), 109 | right: Box::new(Expression::BinaryExpression(BinaryExpressionData { 110 | op: BinaryOperator::Implication, 111 | left: Box::new(not_condition.clone()), 112 | right: Box::new(wp_else.unwrap()) 113 | })) 114 | })); 115 | }, 116 | // Unimplemented TerminatorKinds 117 | // DropAndReplace{location, value, target, unwind} 118 | TerminatorKind::DropAndReplace{..} => unimplemented!(), 119 | // Drop{location, target, unwind} 120 | TerminatorKind::Drop{..} => unimplemented!(), 121 | TerminatorKind::Unreachable => unimplemented!(), 122 | TerminatorKind::Resume => unimplemented!(), 123 | // Switch{discr, adt_def, targets} 124 | TerminatorKind::Switch{..} => unimplemented!(), 125 | // SwitchInt{discr, switch_ty, values, targets} 126 | TerminatorKind::SwitchInt{..} => unimplemented!(), 127 | } 128 | 129 | // Examine the statements in reverse order 130 | let mut stmts = data.block_data[index].statements.clone(); 131 | stmts.reverse(); 132 | 133 | // Prints the current BasicBlock index 134 | if debug { 135 | println!("Processing bb{:?}:", index); 136 | } 137 | 138 | for stmt in stmts { 139 | // Modify the weakest precondition based on the statement 140 | wp = gen_stmt(wp.unwrap(), stmt, data, debug); 141 | } 142 | 143 | // Prints the result to be returned to the proceeding block 144 | if debug { 145 | println!("wp returned as\t{:?}\n", wp.clone().unwrap()); 146 | } 147 | 148 | // Return the weakest precondition to the preceeding block, or to control 149 | wp 150 | } 151 | 152 | /// Returns a (possibly) modified weakest precondition based on the content of a statement 153 | /// 154 | /// # Arguments: 155 | /// * `wp` - The current weakest precondition 156 | /// * `stmt` - The statement to be processed. 157 | /// * `data` - Contains the `BasicBlockData` and all argument, temp, and variable declarations from 158 | /// the MIR pass. 159 | /// 160 | /// # Return Value: 161 | /// * Returns the modified weakest precondition with underflow check 162 | /// 163 | /// # Remarks: 164 | /// 165 | fn gen_stmt(mut wp: Expression, stmt: Statement, data: &mut MirData, debug: bool) 166 | -> Option { 167 | // Prints the current statement being processed. 168 | if debug { 169 | println!("processing statement\t{:?}\ninto expression\t\t{:?}", stmt, wp); 170 | } 171 | 172 | let lvalue: Option; 173 | let rvalue: Option; 174 | 175 | // Store the values of the statement 176 | match stmt.kind { 177 | StatementKind::Assign(ref lval, ref rval) => { 178 | lvalue = Some(lval.clone()); 179 | rvalue = Some(rval.clone()); 180 | }, 181 | //_ => return Some(wp) 182 | } 183 | // The variable or temp on the left-hand side of the assignment 184 | let mut var = gen_lvalue(lvalue.unwrap(), data); 185 | 186 | // The expression on the right-hand side of the assignment 187 | let mut expression = Vec::new(); 188 | match rvalue.clone().unwrap() { 189 | Rvalue::CheckedBinaryOp(ref binop, ref loperand, ref roperand) => { 190 | let lvalue: Expression = gen_expression(loperand, data); 191 | let rvalue: Expression = gen_expression(roperand, data); 192 | let op: BinaryOperator = match *binop { 193 | BinOp::Add => { 194 | // Add the overflow expression checks 195 | wp = overflow::overflow_check(&wp, &var, binop, &lvalue, &rvalue); 196 | BinaryOperator::Addition 197 | }, 198 | BinOp::Sub => { 199 | // Add the overflow and underflow expression checks 200 | wp = overflow::overflow_check(&wp, &var, binop, &lvalue, &rvalue); 201 | BinaryOperator::Subtraction 202 | }, 203 | BinOp::Mul => { 204 | // Add the overflow and underflow expression checks 205 | wp = overflow::overflow_check(&wp, &var, binop, &lvalue, &rvalue); 206 | BinaryOperator::Multiplication 207 | }, 208 | BinOp::Div => { 209 | // Add the overflow and underflow expression checks, if operands are signed 210 | if is_signed_type(determine_evaluation_type(&rvalue)) { 211 | wp = overflow::overflow_check(&wp, &var, binop, &lvalue, &rvalue); 212 | } 213 | // Add the division by 0 expression check 214 | wp = add_zero_check(&wp, &rvalue); 215 | BinaryOperator::Division 216 | }, 217 | BinOp::Rem => { 218 | // Add the overflow and underflow expression checks, if operands are signed 219 | if is_signed_type(determine_evaluation_type(&rvalue)) { 220 | wp = overflow::overflow_check(&wp, &var, binop, &lvalue, &rvalue); 221 | } 222 | // Add the division by 0 expression check 223 | wp = add_zero_check(&wp, &rvalue); 224 | BinaryOperator::Modulo 225 | }, 226 | BinOp::Shl => BinaryOperator::BitwiseLeftShift, 227 | BinOp::Shr => BinaryOperator::BitwiseRightShift, 228 | _ => rp_error!("Unsupported checked binary operation!"), 229 | }; 230 | 231 | var.name = var.name + ".0"; 232 | 233 | // Add the new BinaryExpressionData to the expression vector 234 | expression.push(Expression::BinaryExpression( BinaryExpressionData { 235 | op: op, 236 | left: Box::new(lvalue), 237 | right: Box::new(rvalue) 238 | } )); 239 | }, 240 | 241 | Rvalue::BinaryOp(ref binop, ref lval, ref rval) => { 242 | let lvalue: Expression = gen_expression(lval, data); 243 | let rvalue: Expression = gen_expression(rval, data); 244 | let op: BinaryOperator = match *binop { 245 | BinOp::Add => { 246 | // Add the overflow expression check 247 | wp = overflow::overflow_check(&wp, &var, binop, &lvalue, &rvalue); 248 | BinaryOperator::Addition 249 | }, 250 | BinOp::Sub => { 251 | // Add the overflow and underflow expression checks 252 | wp = overflow::overflow_check(&wp, &var, binop, &lvalue, &rvalue); 253 | BinaryOperator::Subtraction 254 | }, 255 | BinOp::Mul => { 256 | // Add the overflow and underflow expression checks 257 | wp = overflow::overflow_check(&wp, &var, binop, &lvalue, &rvalue); 258 | BinaryOperator::Multiplication 259 | }, 260 | BinOp::Div => { 261 | // Add the overflow and underflow expression checks, if operands are signed 262 | if is_signed_type(determine_evaluation_type(&rvalue)) { 263 | wp = overflow::overflow_check(&wp, &var, binop, &lvalue, &rvalue); 264 | } 265 | // Add the division by 0 expression check 266 | wp = add_zero_check(&wp, &rvalue); 267 | BinaryOperator::Division 268 | }, 269 | BinOp::Rem => { 270 | // Add the overflow and underflow expression checks, if operands are signed 271 | if is_signed_type(determine_evaluation_type(&rvalue)) { 272 | wp = overflow::overflow_check(&wp, &var, binop, &lvalue, &rvalue); 273 | } 274 | // Add the division by 0 expression check 275 | wp = add_zero_check(&wp, &rvalue); 276 | BinaryOperator::Modulo 277 | }, 278 | BinOp::BitOr => BinaryOperator::BitwiseOr, 279 | BinOp::BitAnd => BinaryOperator::BitwiseAnd, 280 | BinOp::BitXor => BinaryOperator::BitwiseXor, 281 | BinOp::Shl => BinaryOperator::BitwiseLeftShift, 282 | BinOp::Shr => BinaryOperator::BitwiseRightShift, 283 | BinOp::Lt => BinaryOperator::LessThan, 284 | BinOp::Le => BinaryOperator::LessThanOrEqual, 285 | BinOp::Gt => BinaryOperator::GreaterThan, 286 | BinOp::Ge => BinaryOperator::GreaterThanOrEqual, 287 | BinOp::Eq => BinaryOperator::Equal, 288 | BinOp::Ne => BinaryOperator::NotEqual, 289 | }; 290 | // Add the expression to the vector 291 | expression.push(Expression::BinaryExpression( BinaryExpressionData { 292 | op: op, 293 | left: Box::new(lvalue), 294 | right: Box::new(rvalue) 295 | } )); 296 | }, 297 | // Generates Rvalue to a UnaryOp 298 | Rvalue::UnaryOp(ref unop, ref val) => { 299 | let exp: Expression = gen_expression(val, data); 300 | let op: UnaryOperator = match *unop { 301 | UnOp::Not => { 302 | if determine_evaluation_type(&exp) == Types::Bool { 303 | UnaryOperator::Not 304 | } else { 305 | UnaryOperator::BitwiseNot 306 | } 307 | }, 308 | UnOp::Neg => UnaryOperator::Negation, 309 | }; 310 | // push the ne new exp onto the expression: Vec<> 311 | expression.push(Expression::UnaryExpression( UnaryExpressionData { 312 | op: op, 313 | e: Box::new(exp) 314 | } )); 315 | }, 316 | // FIXME: need def 317 | Rvalue::Use(ref operand) => { 318 | expression.push(gen_expression(operand, data)); 319 | }, 320 | // FIXME: need def 321 | Rvalue::Aggregate(ref ag_kind, ref vec_operand) => { 322 | match *ag_kind { 323 | AggregateKind::Tuple => { 324 | for operand in vec_operand.iter() { 325 | let e = Expression::VariableMapping( VariableMappingData { 326 | //name: var.name.as_str().to_string() + "." + i.to_string().as_str(), 327 | name: format!("{:?}", operand), 328 | var_type: gen_ty(operand, data) 329 | } ); 330 | expression.push(e); 331 | } 332 | }, 333 | _ => rp_error!("Unsupported aggregate: only tuples are supported"), 334 | } 335 | }, 336 | // FIXME: need def 337 | // Cast(ref cast_kind, ref cast_operand, ref cast_ty) 338 | Rvalue::Cast(..) => { 339 | expression.push(Expression::VariableMapping(var.clone())); 340 | }, 341 | // FIXME: need def 342 | // Ref(ref ref_region, ref ref_borrow_kind, ref ref_lvalue) => { 343 | Rvalue::Ref(..) => { 344 | expression.push(Expression::VariableMapping(var.clone())); 345 | }, 346 | // Unimplemented Rvalues 347 | Rvalue::Box(..) => unimplemented!(), 348 | Rvalue::Len(..) => unimplemented!(), 349 | _ => unimplemented!(), 350 | }; 351 | 352 | // Replace any appearance of var in the weakest precondition with the expression 353 | for expr in &expression { 354 | substitute_variable_with_expression( &mut wp, &var, expr ); 355 | } 356 | // Prints the new weakest precondition 357 | if debug { 358 | println!("new expression\t\t{:?}\n--------------------------------", wp.clone()); 359 | } 360 | return Some(wp); 361 | } 362 | 363 | /// Returns the type of an operand as a `Types` 364 | /// 365 | /// # Arguments: 366 | /// * `operand` - The operand whose type is being returned. 367 | /// * `data` - Contains the `BasicBlockData` and all argument, temp, and variable declarations from 368 | /// the MIR pass. 369 | /// 370 | /// # Remarks: 371 | /// 372 | fn gen_ty(operand: &Operand, data: &mut MirData) -> Types { 373 | let type_string: String = match operand.clone() { 374 | Operand::Constant(ref constant) => constant.ty.to_string(), 375 | Operand::Consume(ref lvalue) => { 376 | match *lvalue { 377 | // Function argument 378 | Lvalue::Arg(ref arg) => data.arg_data[arg.index()].ty.to_string(), 379 | // Temporary variable 380 | Lvalue::Temp(ref temp) => data.temp_data[temp.index()].ty.to_string(), 381 | // Local variable 382 | Lvalue::Var(ref var) => data.var_data[var.index()].ty.to_string(), 383 | _ => unimplemented!(), 384 | } 385 | }, 386 | }; 387 | 388 | string_to_type(type_string) 389 | } 390 | 391 | /// Generates a version of wp "And"ed together with a conditional expression that mimics a check 392 | /// to ensure division by 0 does not occur. 393 | /// 394 | /// # Arguments: 395 | /// * `wp` - The current weakest precondition that the "div by 0" is to be "And"ed to 396 | /// * `exp` - The expression to check to make sure it is not divided by 0 397 | /// 398 | /// # Return Value: 399 | /// * Returns the modified weakest precondition with "div by 0" Expression "And"ed 400 | /// 401 | /// # Remarks: 402 | /// * Currently supported `ConstInt`: `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64` 403 | /// 404 | fn add_zero_check(wp: &Expression, exp: &Expression) -> Expression { 405 | let zero; 406 | if is_signed_type(determine_evaluation_type(exp)) { 407 | zero = Expression::SignedBitVector( SignedBitVectorData { 408 | // The bit-vector size of the given type 409 | size: match determine_evaluation_type(exp) { 410 | Types::I8 => 8, 411 | Types::I16 => 16, 412 | Types::I32 => 32, 413 | Types::I64 => 64, 414 | _ => rp_error!("Unimplemented checkeddAdd right-hand operand type"), 415 | }, 416 | value: 0 417 | }); 418 | } else { 419 | zero = Expression::UnsignedBitVector( UnsignedBitVectorData { 420 | // The bit-vector size of the given type 421 | size: match determine_evaluation_type(exp) { 422 | Types::U8 => 8, 423 | Types::U16 => 16, 424 | Types::U32 => 32, 425 | Types::U64 => 64, 426 | _ => rp_error!("Unimplemented checkeddAdd right-hand operand type"), 427 | }, 428 | value: 0 429 | }); 430 | } 431 | 432 | Expression::BinaryExpression( BinaryExpressionData{ 433 | // And the weakest precondtion and the zero check 434 | op: BinaryOperator::And, 435 | left: Box::new(wp.clone()), 436 | right: Box::new(Expression::BinaryExpression( BinaryExpressionData{ 437 | op: BinaryOperator::NotEqual, 438 | // The expression to be checked 439 | left: Box::new(exp.clone()), 440 | // Need to set appropriate type with value of 0 441 | right: Box::new(zero), 442 | })) 443 | }) 444 | } 445 | 446 | /// Generates an appropriate variable mapping based on whatever variable, temp, or field is found 447 | /// 448 | /// # Arguments: 449 | /// * `lvalue` - The left value of an assignment to be generated into a `VariableMapping` 450 | /// * `data` - Contains the `BasicBlockData` and all argument, temp, and variable declarations from 451 | /// the MIR pass. 452 | /// 453 | /// # Return Value: 454 | /// * Returns a `VariableMappingData` that is built from `data` and `lvalue` 455 | /// 456 | /// # Remarks: 457 | /// 458 | fn gen_lvalue(lvalue: Lvalue, data: &mut MirData) -> VariableMappingData { 459 | match lvalue { 460 | // Function argument 461 | Lvalue::Arg(ref arg) => { 462 | // Find the name and type in the declaration 463 | VariableMappingData{ 464 | name: data.arg_data[arg.index()].debug_name.as_str().to_string(), 465 | var_type: string_to_type(data.arg_data[arg.index()].ty.clone().to_string()) 466 | } 467 | }, 468 | // Temporary variable 469 | Lvalue::Temp(ref temp) => { 470 | // Find the index and type in the declaration 471 | let mut ty = data.temp_data[temp.index()].ty.clone().to_string(); 472 | if let TypeVariants::TyTuple(t) = data.temp_data[temp.index()].ty.sty { 473 | if t.len() > 0 { 474 | ty = t[0].to_string(); 475 | } 476 | } 477 | VariableMappingData{ 478 | name: "tmp".to_string() + temp.index().to_string().as_str(), 479 | var_type: string_to_type(ty) 480 | } 481 | }, 482 | // Local variable 483 | Lvalue::Var(ref var) => { 484 | // Find the name and type in the declaration 485 | VariableMappingData{ 486 | name: "var".to_string() + var.index().to_string().as_str(), 487 | var_type: string_to_type(data.var_data[var.index()].ty.clone().to_string()) 488 | } 489 | }, 490 | // The returned value 491 | Lvalue::ReturnPointer => { 492 | VariableMappingData{ 493 | name: "return".to_string(), 494 | var_type: data.func_return_type.clone() 495 | } 496 | }, 497 | // (Most likely) a field of a tuple from a checked operation 498 | Lvalue::Projection(pro) => { 499 | 500 | // Get the index 501 | let index: String = match pro.as_ref().elem.clone() { 502 | // Index(ref o) 503 | ProjectionElem::Index(_) => unimplemented!(), 504 | // Field(ref field, ref ty) 505 | ProjectionElem::Field(ref field, _) => (field.index() as i32).to_string(), 506 | _ => unimplemented!(), 507 | }; 508 | 509 | // Get the name of the variable being projected 510 | let lvalue_name; 511 | let lvalue_type_string; 512 | 513 | match pro.as_ref().base { 514 | // Argument 515 | Lvalue::Arg(ref arg) => { 516 | // Return the name of the argument 517 | lvalue_name = data.arg_data[arg.index()].debug_name.as_str().to_string(); 518 | lvalue_type_string = data.arg_data[arg.index()].ty.clone().to_string(); 519 | }, 520 | // Temporary variable 521 | Lvalue::Temp(ref temp) => { 522 | // Return "temp" 523 | lvalue_name = "tmp".to_string() + temp.index().to_string().as_str(); 524 | 525 | match data.temp_data[temp.index()].ty.sty { 526 | TypeVariants::TyTuple(t) => lvalue_type_string = t[0].to_string(), 527 | _ => unimplemented!(), 528 | } 529 | }, 530 | // Local variable 531 | Lvalue::Var(ref var) => { 532 | // Return the name of the variable 533 | let i = index.parse::().unwrap(); 534 | lvalue_name = "var".to_string() + var.index().to_string().as_str(); 535 | 536 | match data.var_data[var.index()].ty.sty { 537 | TypeVariants::TyTuple(t) => lvalue_type_string = t[i].to_string(), 538 | _ => unimplemented!(), 539 | } 540 | }, 541 | // Unimplemented Lvalue 542 | Lvalue::ReturnPointer => unimplemented!(), 543 | // Static(ref stat) 544 | Lvalue::Static(_) => unimplemented!(), 545 | // Multiply-nested projection 546 | Lvalue::Projection(_) => unimplemented!(), 547 | }; 548 | 549 | // Get the index 550 | let index: String = match pro.as_ref().elem.clone() { 551 | 552 | // Field(ref field, ref ty) 553 | ProjectionElem::Field(ref field, _) => (field.index() as i32).to_string(), 554 | // Index(ref o) 555 | ProjectionElem::Index(_) => unimplemented!(), 556 | _ => unimplemented!(), 557 | }; 558 | 559 | let lvalue_type: Types = string_to_type(lvalue_type_string); 560 | 561 | // Get the index int from index_operand, then stick it in the VariableMappingData 562 | VariableMappingData{ name: lvalue_name + "." + index.as_str(), var_type: lvalue_type } 563 | }, 564 | _=> unimplemented!(), 565 | } 566 | } 567 | 568 | /// Generates an Expression based on some operand, either a literal or some kind of variable, temp, 569 | /// or field 570 | /// 571 | /// # Arguments: 572 | /// * `operand` - The operand to generate a new expression from. 573 | /// * `data` - Contains the `BasicBlockData` and all argument, temp, and variable declarations from 574 | /// the MIR pass. 575 | /// 576 | /// # Return Value: 577 | /// * Returns a new expression generated from an operand 578 | /// 579 | /// # Remarks: 580 | /// * Current supported types: `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64`, `bool` 581 | /// 582 | fn gen_expression(operand: &Operand, data: &mut MirData) -> Expression { 583 | match *operand { 584 | // A variable/temp/field 585 | Operand::Consume (ref l) => { 586 | Expression::VariableMapping( gen_lvalue(l.clone(), data) ) 587 | }, 588 | // A literal value 589 | Operand::Constant (ref c) => { 590 | match c.literal { 591 | Literal::Value {ref value} => { 592 | match *value { 593 | ConstVal::Bool(ref const_bool) => { 594 | Expression::BooleanLiteral(*const_bool) 595 | } 596 | ConstVal::Integral(ref const_int) => { 597 | match *const_int { 598 | ConstInt::I8(i) => { 599 | Expression::SignedBitVector( SignedBitVectorData { 600 | size: 8, 601 | value: i as i64 602 | } ) 603 | }, 604 | ConstInt::I16(i) => { 605 | Expression::SignedBitVector( SignedBitVectorData { 606 | size: 16, 607 | value: i as i64 608 | } ) 609 | }, 610 | ConstInt::I32(i) => { 611 | Expression::SignedBitVector( SignedBitVectorData { 612 | size: 32, 613 | value: i as i64 614 | } ) 615 | }, 616 | ConstInt::I64(i) => { 617 | Expression::SignedBitVector( SignedBitVectorData { 618 | size: 64, 619 | value: i as i64 620 | } ) 621 | }, 622 | ConstInt::U8(u) => { 623 | Expression::UnsignedBitVector( UnsignedBitVectorData { 624 | size: 8, 625 | value: u as u64 626 | } ) 627 | }, 628 | ConstInt::U16(u) => { 629 | Expression::UnsignedBitVector( UnsignedBitVectorData { 630 | size: 16, 631 | value: u as u64 632 | } ) 633 | }, 634 | ConstInt::U32(u) => { 635 | Expression::UnsignedBitVector( UnsignedBitVectorData { 636 | size: 32, 637 | value: u as u64 638 | } ) 639 | }, 640 | ConstInt::U64(u) => { 641 | Expression::UnsignedBitVector( UnsignedBitVectorData { 642 | size: 64, 643 | value: u as u64 644 | } ) 645 | }, 646 | _ => unimplemented!(), 647 | } 648 | }, 649 | _ => unimplemented!(), 650 | } 651 | }, 652 | // Item {ref def_id, ref substs} 653 | Literal::Item {..} => unimplemented!(), 654 | // Promoted {ref index} 655 | Literal::Promoted {..} => unimplemented!(), 656 | } 657 | }, 658 | } 659 | } 660 | -------------------------------------------------------------------------------- /src/weakest_precondition/overflow.rs: -------------------------------------------------------------------------------- 1 | // The Rust-Proof Project is copyright 2016, Sami Sahli, 2 | // Michael Salter, Matthew Slocum, Vincent Schuster, 3 | // Bradley Rasmussen, Drew Gohman, and Matthew O'Brien. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Functions to generate overflow checks in the weakest precondition. 12 | 13 | extern crate rustc_const_math; 14 | 15 | use expression::*; 16 | use rustc::mir::repr::*; 17 | 18 | 19 | /// Routes to appropriate overflow check (signed / unsigned) 20 | // One catch-all function for overflow checking. 21 | pub fn overflow_check(wp: &Expression, 22 | var: &VariableMappingData, 23 | binop: &BinOp, 24 | lvalue: &Expression, 25 | rvalue: &Expression) 26 | -> Expression { 27 | Expression::BinaryExpression( BinaryExpressionData { 28 | op: BinaryOperator::And, 29 | left: Box::new(wp.clone()), 30 | right: Box::new( 31 | match var.var_type { 32 | Types::I8 => signed_overflow(binop, 8u8, lvalue, rvalue), 33 | Types::I16 => signed_overflow(binop, 16u8, lvalue, rvalue), 34 | Types::I32 => signed_overflow(binop, 32u8, lvalue, rvalue), 35 | Types::I64 => signed_overflow(binop, 64u8, lvalue, rvalue), 36 | Types::U8 | Types::U16 | Types::U32 | Types::U64 => { 37 | unsigned_overflow(binop, lvalue, rvalue) 38 | }, 39 | _ => panic!("Unsupported return type of binary operation: {}", var.var_type), 40 | } 41 | ), 42 | }) 43 | } 44 | 45 | /// Routes to appropriate overflow check 46 | // Signed: Match on the type of BinOp and call the correct function 47 | fn signed_overflow(binop: &BinOp, size: u8, lvalue: &Expression, rvalue: &Expression) 48 | -> Expression { 49 | match *binop { 50 | BinOp::Add => signed_add(size, lvalue, rvalue), 51 | BinOp::Mul => signed_mul(lvalue, rvalue), 52 | BinOp::Sub => signed_sub(size, lvalue, rvalue), 53 | BinOp::Div => signed_div(size, lvalue, rvalue), 54 | BinOp::Rem => signed_div(size, lvalue, rvalue), 55 | BinOp::Shl => unimplemented!(), 56 | BinOp::Shr => unimplemented!(), 57 | BinOp::BitOr => unimplemented!(), 58 | BinOp::BitAnd => unimplemented!(), 59 | BinOp::BitXor => unimplemented!(), 60 | BinOp::Lt => unimplemented!(), 61 | BinOp::Le => unimplemented!(), 62 | BinOp::Gt => unimplemented!(), 63 | BinOp::Ge => unimplemented!(), 64 | BinOp::Eq => unimplemented!(), 65 | BinOp::Ne => unimplemented!(), 66 | } 67 | } 68 | 69 | /// Creates an Expression containing overflow and underflow checks for lvalue + rvalue, assuming 70 | /// they are bitvectors of length "size" 71 | /// 72 | /// The following psuedocode provides a logically equivalent version of what is produced 73 | /// (false is returned if overflow/underflow has occurred, true otherwise) 74 | /// 75 | /// ```psuedo 76 | /// If lvalue >= 0 && rvalue >= 0 77 | /// If lvalue + rvalue < 0 78 | /// false 79 | /// Else 80 | /// true 81 | /// Else 82 | /// If lvalue < 0 && rvalue < 0 83 | /// If lvalue + rvalue >= 0 84 | /// false 85 | /// Else 86 | /// true 87 | /// Else 88 | /// true 89 | /// ``` 90 | fn signed_add(size: u8, lvalue: &Expression, rvalue: &Expression) -> Expression { 91 | Expression::BinaryExpression( BinaryExpressionData{ 92 | op: BinaryOperator::And, 93 | left: Box::new( 94 | Expression::BinaryExpression( BinaryExpressionData{ 95 | op: BinaryOperator::Implication, 96 | left: Box::new( 97 | Expression::BinaryExpression( BinaryExpressionData{ 98 | op: BinaryOperator::And, 99 | left: Box::new( 100 | Expression::BinaryExpression( BinaryExpressionData{ 101 | op: BinaryOperator::GreaterThanOrEqual, 102 | left: Box::new(lvalue.clone()), 103 | right: Box::new( 104 | Expression::SignedBitVector( SignedBitVectorData { 105 | size: size, 106 | value: 0i64, 107 | }) 108 | ), 109 | }) 110 | ), 111 | right: Box::new( 112 | Expression::BinaryExpression( BinaryExpressionData{ 113 | op: BinaryOperator::GreaterThanOrEqual, 114 | left: Box::new(rvalue.clone()), 115 | right: Box::new( 116 | Expression::SignedBitVector( SignedBitVectorData { 117 | size: size, 118 | value: 0i64, 119 | }) 120 | ), 121 | }) 122 | ), 123 | }) 124 | ), 125 | right: Box::new( 126 | Expression::BinaryExpression( BinaryExpressionData{ 127 | op: BinaryOperator::GreaterThanOrEqual, 128 | left: Box::new( 129 | Expression::BinaryExpression( BinaryExpressionData{ 130 | op: BinaryOperator::Addition, 131 | left: Box::new(lvalue.clone()), 132 | right: Box::new(rvalue.clone()), 133 | }) 134 | ), 135 | right: Box::new( 136 | Expression::SignedBitVector( SignedBitVectorData { 137 | size: size, 138 | value: 0i64, 139 | }) 140 | ), 141 | }) 142 | ), 143 | }) 144 | ), 145 | right: Box::new( 146 | Expression::BinaryExpression( BinaryExpressionData{ 147 | op: BinaryOperator::Implication, 148 | left: Box::new( 149 | Expression::BinaryExpression( BinaryExpressionData{ 150 | op: BinaryOperator::Or, 151 | left: Box::new( 152 | Expression::BinaryExpression( BinaryExpressionData{ 153 | op: BinaryOperator::LessThan, 154 | left: Box::new(lvalue.clone()), 155 | right: Box::new( 156 | Expression::SignedBitVector( SignedBitVectorData { 157 | size: size, 158 | value: 0i64, 159 | }) 160 | ), 161 | }) 162 | ), 163 | right: Box::new( 164 | Expression::BinaryExpression( BinaryExpressionData{ 165 | op: BinaryOperator::LessThan, 166 | left: Box::new(rvalue.clone()), 167 | right: Box::new( 168 | Expression::SignedBitVector( SignedBitVectorData { 169 | size: size, 170 | value: 0i64, 171 | }) 172 | ), 173 | }) 174 | ), 175 | }) 176 | ), 177 | right: Box::new( 178 | Expression::BinaryExpression( BinaryExpressionData{ 179 | op: BinaryOperator::Implication, 180 | left: Box::new( 181 | Expression::BinaryExpression( BinaryExpressionData{ 182 | op: BinaryOperator::And, 183 | left: Box::new( 184 | Expression::BinaryExpression( BinaryExpressionData{ 185 | op: BinaryOperator::LessThan, 186 | left: Box::new(lvalue.clone()), 187 | right: Box::new( 188 | Expression::SignedBitVector( SignedBitVectorData { 189 | size: size, 190 | value: 0i64, 191 | }) 192 | ), 193 | }) 194 | ), 195 | right: Box::new( 196 | Expression::BinaryExpression( BinaryExpressionData{ 197 | op: BinaryOperator::LessThan, 198 | left: Box::new(rvalue.clone()), 199 | right: Box::new( 200 | Expression::SignedBitVector( SignedBitVectorData { 201 | size: size, 202 | value: 0i64, 203 | }) 204 | ), 205 | }) 206 | ), 207 | }) 208 | ), 209 | right: Box::new( 210 | Expression::BinaryExpression( BinaryExpressionData{ 211 | op: BinaryOperator::LessThan, 212 | left: Box::new( 213 | Expression::BinaryExpression( BinaryExpressionData{ 214 | op: BinaryOperator::Addition, 215 | left: Box::new(lvalue.clone()), 216 | right: Box::new(rvalue.clone()), 217 | }) 218 | ), 219 | right: Box::new( 220 | Expression::SignedBitVector( SignedBitVectorData { 221 | size: size, 222 | value: 0i64, 223 | }) 224 | ), 225 | }) 226 | ), 227 | }) 228 | ), 229 | }) 230 | ), 231 | }) 232 | } 233 | 234 | /// Creates an Expression containing overflow and underflow checks for lvalue - rvalue, assuming 235 | /// they are bitvectors of length "size" 236 | /// 237 | /// The following psuedocode provides a logically equivalent version of what is produced 238 | /// (false is returned if overflow/underflow has occurred, true otherwise) 239 | /// 240 | /// ```psuedo 241 | /// If lvalue >= 0 && rvalue < 0 242 | /// If lvalue - rvalue < 0 243 | /// false 244 | /// Else 245 | /// true 246 | /// Else 247 | /// If lvalue < 0 && rvalue >= 0 248 | /// If lvalue - rvalue >= 0 249 | /// false 250 | /// Else 251 | /// true 252 | /// Else 253 | /// true 254 | /// ``` 255 | fn signed_sub(size: u8, lvalue: &Expression, rvalue: &Expression) -> Expression { 256 | Expression::BinaryExpression( BinaryExpressionData{ 257 | op: BinaryOperator::And, 258 | left: Box::new( 259 | Expression::BinaryExpression( BinaryExpressionData{ 260 | op: BinaryOperator::Implication, 261 | left: Box::new( 262 | Expression::BinaryExpression( BinaryExpressionData{ 263 | op: BinaryOperator::And, 264 | left: Box::new( 265 | Expression::BinaryExpression( BinaryExpressionData{ 266 | op: BinaryOperator::GreaterThanOrEqual, 267 | left: Box::new(lvalue.clone()), 268 | right: Box::new( 269 | Expression::SignedBitVector( SignedBitVectorData { 270 | size: size, 271 | value: 0i64, 272 | }) 273 | ), 274 | }) 275 | ), 276 | right: Box::new( 277 | Expression::BinaryExpression( BinaryExpressionData{ 278 | op: BinaryOperator::LessThan, 279 | left: Box::new(rvalue.clone()), 280 | right: Box::new( 281 | Expression::SignedBitVector( SignedBitVectorData { 282 | size: size, 283 | value: 0i64, 284 | }) 285 | ), 286 | }) 287 | ), 288 | }) 289 | ), 290 | right: Box::new( 291 | Expression::BinaryExpression( BinaryExpressionData{ 292 | op: BinaryOperator::GreaterThanOrEqual, 293 | left: Box::new( 294 | Expression::BinaryExpression( BinaryExpressionData{ 295 | op: BinaryOperator::Subtraction, 296 | left: Box::new(lvalue.clone()), 297 | right: Box::new(rvalue.clone()), 298 | }) 299 | ), 300 | right: Box::new( 301 | Expression::SignedBitVector( SignedBitVectorData { 302 | size: size, 303 | value: 0i64, 304 | }) 305 | ), 306 | }) 307 | ), 308 | }) 309 | ), 310 | right: Box::new( 311 | Expression::BinaryExpression( BinaryExpressionData{ 312 | op: BinaryOperator::Implication, 313 | left: Box::new( 314 | Expression::BinaryExpression( BinaryExpressionData{ 315 | op: BinaryOperator::Or, 316 | left: Box::new( 317 | Expression::BinaryExpression( BinaryExpressionData{ 318 | op: BinaryOperator::LessThan, 319 | left: Box::new(lvalue.clone()), 320 | right: Box::new( 321 | Expression::SignedBitVector( SignedBitVectorData { 322 | size: size, 323 | value: 0i64, 324 | }) 325 | ), 326 | }) 327 | ), 328 | right: Box::new( 329 | Expression::BinaryExpression( BinaryExpressionData{ 330 | op: BinaryOperator::GreaterThanOrEqual, 331 | left: Box::new(rvalue.clone()), 332 | right: Box::new( 333 | Expression::SignedBitVector( SignedBitVectorData { 334 | size: size, 335 | value: 0i64, 336 | }) 337 | ), 338 | }) 339 | ), 340 | }) 341 | ), 342 | right: Box::new( 343 | Expression::BinaryExpression( BinaryExpressionData{ 344 | op: BinaryOperator::Implication, 345 | left: Box::new( 346 | Expression::BinaryExpression( BinaryExpressionData{ 347 | op: BinaryOperator::And, 348 | left: Box::new( 349 | Expression::BinaryExpression( BinaryExpressionData{ 350 | op: BinaryOperator::LessThan, 351 | left: Box::new(lvalue.clone()), 352 | right: Box::new( 353 | Expression::SignedBitVector( SignedBitVectorData { 354 | size: size, 355 | value: 0i64, 356 | }) 357 | ), 358 | }) 359 | ), 360 | right: Box::new( 361 | Expression::BinaryExpression( BinaryExpressionData{ 362 | op: BinaryOperator::GreaterThanOrEqual, 363 | left: Box::new(rvalue.clone()), 364 | right: Box::new( 365 | Expression::SignedBitVector( SignedBitVectorData { 366 | size: size, 367 | value: 0i64, 368 | }) 369 | ), 370 | }) 371 | ), 372 | }) 373 | ), 374 | right: Box::new( 375 | Expression::BinaryExpression( BinaryExpressionData{ 376 | op: BinaryOperator::LessThan, 377 | left: Box::new( 378 | Expression::BinaryExpression( BinaryExpressionData{ 379 | op: BinaryOperator::Subtraction, 380 | left: Box::new(lvalue.clone()), 381 | right: Box::new(rvalue.clone()), 382 | }) 383 | ), 384 | right: Box::new( 385 | Expression::SignedBitVector( SignedBitVectorData { 386 | size: size, 387 | value: 0i64, 388 | }) 389 | ), 390 | }) 391 | ), 392 | }) 393 | ), 394 | }) 395 | ), 396 | }) 397 | } 398 | 399 | fn signed_mul(lvalue: &Expression, rvalue: &Expression) -> Expression { 400 | let overflow: Expression = Expression::BinaryExpression( BinaryExpressionData{ 401 | op: BinaryOperator::SignedMultiplicationDoesNotOverflow, 402 | left: Box::new(lvalue.clone()), 403 | right: Box::new(rvalue.clone()), 404 | }); 405 | 406 | let underflow: Expression = Expression::BinaryExpression( BinaryExpressionData{ 407 | op: BinaryOperator::SignedMultiplicationDoesNotUnderflow, 408 | left: Box::new(lvalue.clone()), 409 | right: Box::new(rvalue.clone()), 410 | }); 411 | 412 | Expression::BinaryExpression( BinaryExpressionData{ 413 | op: BinaryOperator::And, 414 | left: Box::new(overflow), 415 | right: Box::new(underflow), 416 | }) 417 | } 418 | 419 | fn signed_div(size: u8, lvalue: &Expression, rvalue: &Expression) -> Expression { 420 | let condition = Expression::BinaryExpression( BinaryExpressionData{ 421 | op: BinaryOperator::And, 422 | left: Box::new( 423 | Expression::BinaryExpression( BinaryExpressionData{ 424 | op: BinaryOperator::Equal, 425 | left: Box::new(lvalue.clone()), 426 | right: Box::new( 427 | Expression::SignedBitVector( SignedBitVectorData{ 428 | size: size, 429 | value: match size { 430 | 8u8 => i8::min_value() as i64, 431 | 16u8 => i16::min_value() as i64, 432 | 32u8 => i32::min_value() as i64, 433 | 64u8 => i64::min_value() as i64, 434 | _ => panic!("unsupported integer type"), 435 | }, 436 | }) 437 | ), 438 | }) 439 | ), 440 | right: Box::new( 441 | Expression::BinaryExpression( BinaryExpressionData{ 442 | op: BinaryOperator::Equal, 443 | left: Box::new(rvalue.clone()), 444 | right: Box::new( 445 | Expression::SignedBitVector( SignedBitVectorData{ 446 | size: size, 447 | value: -1i64, 448 | }) 449 | ) 450 | }) 451 | ), 452 | }); 453 | 454 | Expression::BinaryExpression( BinaryExpressionData{ 455 | op: BinaryOperator::And, 456 | left: Box::new( 457 | Expression::BinaryExpression( BinaryExpressionData{ 458 | op: BinaryOperator::Implication, 459 | left: Box::new(condition.clone()), 460 | right: Box::new( 461 | Expression::BooleanLiteral(false) 462 | ), 463 | }) 464 | ), 465 | right: Box::new( 466 | Expression::BinaryExpression( BinaryExpressionData{ 467 | op: BinaryOperator::Implication, 468 | left: Box::new( 469 | Expression::UnaryExpression( UnaryExpressionData{ 470 | op: UnaryOperator::Not, 471 | e: Box::new(condition.clone()), 472 | }) 473 | ), 474 | right: Box::new( 475 | Expression::BooleanLiteral(true) 476 | ), 477 | }) 478 | ), 479 | }) 480 | } 481 | 482 | /// Routes to appropriate overflow check 483 | // Unsigned: Match on the type of BinOp and call the correct function 484 | fn unsigned_overflow(binop: &BinOp, lvalue: &Expression, rvalue: &Expression) -> Expression { 485 | match *binop { 486 | BinOp::Add => unsigned_add(lvalue, rvalue), 487 | BinOp::Sub => unsigned_sub(lvalue, rvalue), 488 | BinOp::Mul => unsigned_mul(lvalue, rvalue), 489 | BinOp::Div => unreachable!(), 490 | BinOp::Rem => unreachable!(), 491 | BinOp::Shl => unimplemented!(), 492 | BinOp::Shr => unimplemented!(), 493 | BinOp::BitOr => unimplemented!(), 494 | BinOp::BitAnd => unimplemented!(), 495 | BinOp::BitXor => unimplemented!(), 496 | BinOp::Lt => unimplemented!(), 497 | BinOp::Le => unimplemented!(), 498 | BinOp::Gt => unimplemented!(), 499 | BinOp::Ge => unimplemented!(), 500 | BinOp::Eq => unimplemented!(), 501 | BinOp::Ne => unimplemented!(), 502 | } 503 | } 504 | 505 | fn unsigned_mul(lvalue: &Expression, rvalue: &Expression) -> Expression { 506 | Expression::BinaryExpression( BinaryExpressionData{ 507 | op: BinaryOperator::UnsignedMultiplicationDoesNotOverflow, 508 | left: Box::new(lvalue.clone()), 509 | right: Box::new(rvalue.clone()), 510 | }) 511 | } 512 | 513 | // l + r >= l 514 | fn unsigned_add(lvalue: &Expression, rvalue: &Expression) -> Expression { 515 | Expression::BinaryExpression( BinaryExpressionData{ 516 | op: BinaryOperator::GreaterThanOrEqual, 517 | //l + r 518 | left: Box::new( 519 | Expression::BinaryExpression( BinaryExpressionData{ 520 | op: BinaryOperator::Addition, 521 | left: Box::new(lvalue.clone()), 522 | right: Box::new(rvalue.clone()), 523 | }) 524 | ), 525 | // r 526 | right: Box::new(rvalue.clone()), 527 | }) 528 | } 529 | 530 | // Evaluates true when no overflow occurs 531 | // l-r <= l || 532 | // l-r <= r 533 | fn unsigned_sub(lvalue: &Expression, rvalue: &Expression) -> Expression { 534 | // l-r <= l || l-r <= r 535 | Expression::BinaryExpression( BinaryExpressionData{ 536 | op: BinaryOperator::Or, 537 | left: Box::new( 538 | // l-r <= l 539 | Expression::BinaryExpression( BinaryExpressionData{ 540 | op: BinaryOperator::LessThanOrEqual, 541 | // l - r 542 | left: Box::new( 543 | Expression::BinaryExpression( BinaryExpressionData{ 544 | op: BinaryOperator::Subtraction, 545 | left: Box::new(lvalue.clone()), 546 | right: Box::new(rvalue.clone()), 547 | }) 548 | ), 549 | // l 550 | right: Box::new(lvalue.clone()), 551 | }) 552 | ), 553 | right: Box::new( 554 | // l-r <= r 555 | Expression::BinaryExpression( BinaryExpressionData{ 556 | op: BinaryOperator::LessThanOrEqual, 557 | // l - r 558 | left: Box::new( 559 | Expression::BinaryExpression( BinaryExpressionData{ 560 | op: BinaryOperator::Subtraction, 561 | left: Box::new(lvalue.clone()), 562 | right: Box::new(rvalue.clone()), 563 | }) 564 | ), 565 | // r 566 | right: Box::new(rvalue.clone()), 567 | }) 568 | ) 569 | }) 570 | 571 | 572 | 573 | } 574 | -------------------------------------------------------------------------------- /tests/test_assert.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, custom_attribute)] 2 | #![plugin(rustproof)] 3 | #![allow(dead_code)] 4 | #![allow(unused_attributes)] 5 | fn main() { } 6 | 7 | // Should be valid 8 | #[condition(pre="x: i32 == 10i32", post="true")] 9 | fn valid_simple_assertion(x: i32) { 10 | assert!(x > 0); 11 | } 12 | 13 | // Should be invalid 14 | #[condition(pre="x: i32 == 10i32", post="true")] 15 | fn invalid_simple_assertion(x: i32) { 16 | assert!(x < 10); 17 | } 18 | 19 | 20 | //FIXME: This portion doesn't work currently. 21 | /* 22 | // Should be valid 23 | #[condition(pre="x: i32 == 10i32", post="true")] 24 | fn valid_simple_assertion_eq(x: i32) { 25 | assert_eq!(x, 10); 26 | } 27 | 28 | // Should be invalid 29 | #[condition(pre="x: i32 == 10i32", post="true")] 30 | fn invalid_simple_assertion_eq(x: i32) { 31 | assert_eq!(x, 0); 32 | } 33 | */ 34 | -------------------------------------------------------------------------------- /tests/test_boolean_arithmetic.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, custom_attribute)] 2 | #![plugin(rustproof)] 3 | #![allow(dead_code)] 4 | #![allow(unused_attributes)] 5 | fn main() {} 6 | 7 | // * * * 8 | // Boolean and Boolean Operator Tests 9 | // * * * 10 | 11 | // Should be valid 12 | #[condition(pre="true", post="true")] 13 | fn valid_basic_boolean() -> bool { 14 | true 15 | } 16 | 17 | // Should be invalid 18 | #[condition(pre="true", post="false")] 19 | fn invalid_basic_boolean()-> bool { 20 | true 21 | } 22 | 23 | // Should be valid 24 | #[condition(pre="x:bool == true", post="return:bool == true")] 25 | fn valid_boolean_and_logical(x:bool) -> bool { 26 | x && true 27 | } 28 | 29 | // Should be invalid 30 | #[condition(pre="x:bool == true", post="return:bool == true")] 31 | fn invalid_boolean_and_logical(x:bool) -> bool { 32 | x && false 33 | } 34 | 35 | // Should be valid 36 | #[condition(pre="x:bool == true", post="return:bool == true")] 37 | fn valid_boolean_and_bitwise(x:bool) -> bool { 38 | x & true 39 | } 40 | 41 | // Should be invalid 42 | #[condition(pre="x:bool == true", post="return:bool == true")] 43 | fn invalid_boolean_and_bitwise(x:bool) -> bool { 44 | x & false 45 | } 46 | 47 | // Should be valid 48 | #[condition(pre="x:bool == true", post="return:bool == true")] 49 | fn valid_boolean_or_logical(x:bool) -> bool { 50 | x || true 51 | } 52 | // Should be invalid 53 | #[condition(pre="x:bool == true", post="return:bool == false")] 54 | fn invalid_boolean_or_logical(x:bool) -> bool { 55 | x || true 56 | } 57 | 58 | // Should be valid 59 | #[condition(pre="x:bool == true", post="return:bool == true")] 60 | fn valid_boolean_or_bitwise(x:bool) -> bool { 61 | x | true 62 | } 63 | // Should be invalid 64 | #[condition(pre="x:bool == true", post="return:bool == false")] 65 | fn invalid_boolean_or_bitwise(x:bool) -> bool { 66 | x | true 67 | } 68 | 69 | // Should be valid 70 | #[condition(pre="x:bool == true", post="return:bool == false")] 71 | fn valid_boolean_not_variable(x:bool) -> bool { 72 | !x 73 | } 74 | 75 | // Should be invalid 76 | #[condition(pre="x:bool == true", post="return:bool == true")] 77 | fn invalid_boolean_not_variable(x:bool) -> bool { 78 | !x 79 | } 80 | 81 | // Should be valid 82 | #[condition(pre="x:bool == true", post="return: bool == false")] 83 | fn valid_boolean_not_literal() -> bool { 84 | !true 85 | } 86 | 87 | // Should be invalid 88 | #[condition(pre="x:bool == true", post="return: bool == true")] 89 | fn invalid_boolean_not_literal() -> bool { 90 | !true 91 | } 92 | 93 | // Should be valid 94 | #[condition(pre="x:bool == true", post="return: bool == false")] 95 | fn valid_boolean_xor_bitwise(x: bool) -> bool { 96 | x ^ true 97 | } 98 | 99 | // Should be invalid 100 | #[condition(pre="x:bool == true", post="return: bool == true")] 101 | fn invalid_boolean_xor_bitwise(x : bool) -> bool { 102 | x ^ true 103 | } 104 | 105 | // Should be valid 106 | #[condition(pre="true",post="true AND true")] 107 | fn valid_attr_condition_and_and()-> bool { 108 | true 109 | } 110 | 111 | // Should be valid 112 | #[condition(pre="true",post="true && true")] 113 | fn valid_attr_condition_and_logical()-> bool { 114 | true 115 | } 116 | 117 | // Should be valid 118 | #[condition(pre="true",post="true & true")] 119 | fn valid_attr_condition_and_bitwise()-> bool { 120 | true 121 | } 122 | 123 | // Should be valid 124 | #[condition(pre="true",post="true OR true")] 125 | fn valid_attr_condition_or_or()-> bool { 126 | true 127 | } 128 | 129 | // Should be valid 130 | #[condition(pre="true",post="true || true")] 131 | fn valid_attr_condition_or_logical()-> bool { 132 | true 133 | } 134 | 135 | // Should be valid 136 | #[condition(pre="true",post="true | true")] 137 | fn valid_attr_condition_or_bitwise()-> bool { 138 | true 139 | } 140 | 141 | // Should be valid 142 | #[condition(pre="true",post="true ^ false")] 143 | fn valid_attr_condition_xor_bitwise()-> bool { 144 | true 145 | } 146 | 147 | // Should be valid 148 | #[condition(pre="true",post="true XOR false")] 149 | fn valid_attr_condition_xor_xor()-> bool { 150 | true 151 | } 152 | 153 | 154 | // Should be valid 155 | #[condition(pre="x: bool == true", post="return:bool == true")] 156 | fn valid_simple_bool_and(x:bool) -> bool { 157 | x && true 158 | } 159 | 160 | // Should be valid 161 | #[condition(pre="x: bool == false", post="return:bool == false")] 162 | fn valid_simple_bool_or(x:bool) -> bool { 163 | x || false 164 | } 165 | 166 | // Should be invalid 167 | #[condition(pre="x: bool == true", post="return:bool == false")] 168 | fn invalid_simple_bool_and_2(x:bool) -> bool { 169 | x && true 170 | } 171 | 172 | // Should be invalid 173 | #[condition(pre="x: bool == false", post="return:bool == true")] 174 | fn invalid_simple_bool_or_2(x:bool) -> bool { 175 | x || false 176 | } 177 | 178 | // * * * 179 | // test ! Operator 180 | // * * * 181 | 182 | //should be invalid 183 | #[condition(pre="x: bool == true", post="return:bool == true")] 184 | fn invalid_simple_bool_not(x:bool) -> bool { 185 | !x && true 186 | } 187 | 188 | // Should be invalid 189 | #[condition(pre="x: bool == false", post="return:bool == false")] 190 | fn invalid_simple_bool_not_2(x:bool) -> bool { 191 | !x || false 192 | } 193 | 194 | // * * * 195 | // test & and | operator 196 | // * * * 197 | 198 | #[condition(pre="x: bool == true", post="return:bool == true")] 199 | fn valid_simple_bool_bitwise_and(x:bool) -> bool { 200 | x & true 201 | } 202 | 203 | // Should be invalid 204 | #[condition(pre="x: bool == false", post="return:bool == false")] 205 | fn invalid_simple_bool_bitwise_or(x:bool) -> bool { 206 | x | true 207 | } 208 | 209 | // Should be invalid 210 | #[condition(pre="x: bool == true", post="return:bool == true")] 211 | fn invalid_simple_bool_bitwise_and_2(x:bool) -> bool { 212 | x & false 213 | } 214 | 215 | // Should be valid 216 | #[condition(pre="x: bool == true", post="return:bool == true")] 217 | fn valid_simple_bool_bitwise_or_2(x:bool) -> bool { 218 | x | false 219 | } 220 | 221 | // * * * 222 | // Additional Tests 223 | // * * * 224 | 225 | // Should be valid 226 | #[condition(pre="x: bool == false", post="return:bool == true")] 227 | fn valid_simple_bool_bitwise_or_if_else(x:bool) -> bool { 228 | if x { 229 | x | false 230 | } else { 231 | x | true 232 | } 233 | } 234 | 235 | // Should be valid 236 | #[condition(pre="(x: bool == true) || (x: bool == false)", post="return:bool == true")] 237 | fn valid_simple_bool_pre_or(x:bool) -> bool { 238 | if x { 239 | true 240 | } else { 241 | true 242 | } 243 | } 244 | 245 | // Should be invalid 246 | #[condition(pre="(x: bool == true) && (y: bool == false)", post="return:bool == false")] 247 | fn invalid_bool_pre_and(x:bool, y:bool) -> bool { 248 | if x | y { 249 | true 250 | } else { 251 | false 252 | } 253 | } 254 | 255 | // Should be valid 256 | #[condition(pre="x: bool == true", post="(return:bool == true) || (return:bool == false)")] 257 | fn valid_bool_post_or(x:bool) -> bool { 258 | x | false 259 | } 260 | 261 | // Should be valid 262 | #[condition(pre="(x: bool == true) && (y: bool == false) && (z: bool == true)", post="return:bool == true")] 263 | fn valid_bool_complex(x:bool, y:bool, z:bool) -> bool { 264 | let mut a = 4; 265 | 266 | if z { 267 | a = a + 5; 268 | } else { 269 | a = a + 10; 270 | } 271 | 272 | if a > 9 && y { 273 | return true; 274 | } else { 275 | if x { 276 | return true; 277 | } 278 | else { 279 | return false; 280 | } 281 | } 282 | } 283 | 284 | // Should be invalid 285 | #[condition(pre="(w: i32 >= 6i32) && (x: bool == false) && (y: bool == true) && (z: bool == true)", post="return:bool == true")] 286 | fn invalid_bool_complex(x:bool, y:bool, z:bool, w:i32) -> bool { 287 | let mut a = 4; 288 | if w > 5 { 289 | return false; 290 | } 291 | if z { 292 | a = a + 5; 293 | } else { 294 | a = a + 10; 295 | } 296 | 297 | if a > 9 && y { 298 | return true; 299 | } else { 300 | if x { 301 | return true; 302 | } else { 303 | return false; 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /tests/test_conditionals.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, custom_attribute)] 2 | #![plugin(rustproof)] 3 | #![allow(dead_code)] 4 | fn main() {} 5 | 6 | // Tests that should return 'valid' 7 | #[condition(pre="(x: i32 < i32::MAX - 6i32) && (y: bool == false)", post="return: i32 == (x: i32 + 6i32)")] 8 | fn valid_variable_conditional(x: i32, y: bool) -> i32 { 9 | if y { 10 | x + 5 11 | } else { 12 | x + 6 13 | } 14 | } 15 | 16 | #[condition(pre="x: i32 < i32::MAX - 6i32", post="return: i32 == (x: i32 + 6i32)")] 17 | fn valid_literal_conditional(x: i32) -> i32 { 18 | if false { 19 | x + 5 20 | } else { 21 | x + 6 22 | } 23 | } 24 | 25 | #[condition(pre="(x: i32 < i32::MAX - 6i32) && (x: i32 < 5i32)", post="return: i32 == (x: i32 + 6i32)")] 26 | fn valid_expression_conditional(x: i32) -> i32 { 27 | if x > 5 { 28 | x + 5 29 | } else { 30 | x + 6 31 | } 32 | } 33 | 34 | #[condition(pre="x: i32 == 5i32", post="return: i32 == (x: i32 + 6i32)")] 35 | fn valid_triple_branch_conditional(x: i32) -> i32 { 36 | if x > 5 { 37 | x + 5 38 | } else if x < 5 { 39 | x + 7 40 | } else { 41 | x + 6 42 | } 43 | } 44 | 45 | #[condition(pre="(x: i32 == 4i32) && (y: bool == false)", post="return: i32 == (x: i32 + 6i32)")] 46 | fn valid_nested_conditionals(x: i32, y: bool) -> i32 { 47 | if y { 48 | x + 5 49 | } else { 50 | if x > 5 { 51 | x + 7 52 | } else { 53 | x + 6 54 | } 55 | } 56 | } 57 | 58 | // Tests that should return 'invalid' 59 | #[condition(pre="(x: i32 < i32::MAX - 6i32) && (y: bool == true)", post="return: i32 == (x: i32 + 6i32)")] 60 | fn invalid_variable_conditional(x: i32, y: bool) -> i32 { 61 | if y { 62 | x + 5 63 | } else { 64 | x + 6 65 | } 66 | } 67 | 68 | #[condition(pre="x: i32 < i32::MAX - 6i32", post="return: i32 == (x: i32 + 6i32)")] 69 | fn invalid_literal_conditional(x: i32) -> i32 { 70 | if true { 71 | x + 5 72 | } else { 73 | x + 6 74 | } 75 | } 76 | 77 | #[condition(pre="(x: i32 < i32::MAX - 6i32) && (x: i32 < 5i32)", post="return: i32 == (x: i32 + 6i32)")] 78 | fn invalid_expression_conditional(x: i32) -> i32 { 79 | if x < 5 { 80 | x + 5 81 | } else { 82 | x + 6 83 | } 84 | } 85 | 86 | #[condition(pre="x: i32 == 5i32", post="return: i32 == (x: i32 + 6i32)")] 87 | fn invalid_triple_branch_conditional(x: i32) -> i32 { 88 | if x > 5 { 89 | x + 5 90 | } else if x == 5 { 91 | x + 7 92 | } else { 93 | x + 6 94 | } 95 | } 96 | 97 | #[condition(pre="(x: i32 == 4i32) && (y: bool == false)", post="return: i32 == (x: i32 + 6i32)")] 98 | fn invalid_nested_conditionals(x: i32, y: bool) -> i32 { 99 | if y { 100 | x + 5 101 | } else { 102 | if x < 5 { 103 | x + 7 104 | } else { 105 | x + 6 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /tests/test_conditions.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, custom_attribute)] 2 | #![plugin(rustproof)] 3 | #![allow(dead_code)] 4 | #![allow(unused_attributes)] 5 | fn main() { } 6 | 7 | // FIXME: some preconditions are unnecisarilly restricitve ie add_five_i32_invalid does not need x: i32 >= i32::MIN + 5i32 8 | 9 | // * * * 10 | // Integer Add Tests 11 | // * * * 12 | 13 | // Tests signed integer overflow for 32 bit integers 14 | // Should be invalid 15 | #[condition(pre="(x: i32 <= i32::MAX - 4i32) && (x: i32 >= i32::MIN + 5i32)", post="return: i32 == (x: i32 + 5i32)")] 16 | fn invalid_add_five_i32(x: i32) -> i32 { 17 | x+5 18 | } 19 | 20 | // Tests signed integer overflow for 32 bit integers 21 | // Should be valid 22 | #[condition(pre="(x: i32 <= i32::MAX - 5i32) && (x: i32 >= i32::MIN + 5i32)", post="return: i32 == (x: i32 + 5i32)")] 23 | fn valid_add_five_i32(x: i32) -> i32 { 24 | x+5 25 | } 26 | 27 | // Tests signed integer overflow for 64 bit integers 28 | // Should be invalid 29 | #[condition(pre="(x: i64 <= i64::MAX - 4i64) && (x: i64 >= i64::MIN + 5i64)", post="return: i64 == (x: i64 + 5i64)")] 30 | fn invalid_add_five_i64(x: i64) -> i64 { 31 | x+5 32 | } 33 | 34 | // Tests signed integer overflow for 64 bit integers 35 | // Should be valid 36 | #[condition(pre="(x: i64 <= i64::MAX - 5i64) && (x: i64 >= i64::MIN + 5i64)", post="return: i64 == (x: i64 + 5i64)")] 37 | fn valid_add_five_i64(x: i64) -> i64 { 38 | x+5 39 | } 40 | 41 | // Tests unsigned integer overflow for 32 bit integers 42 | // Should be invalid 43 | #[condition(pre="x: u32 <= u32::MAX - 4u32", post="return: u32 == (x: u32 + 5u32)")] 44 | fn invalid_add_five_u32(x: u32) -> u32 { 45 | x+5 46 | } 47 | 48 | // Tests unsigned integer overflow for 32 bit integers 49 | // Should be valid 50 | #[condition(pre="x: u32 <= u32::MAX - 5u32", post="return: u32 == (x: u32 + 5u32)")] 51 | fn valid_add_five_u32(x: u32) -> u32 { 52 | x+5 53 | } 54 | 55 | // * * * 56 | // Assertion Tests 57 | // * * * 58 | 59 | // Should be valid 60 | #[condition(pre="true", post="true")] 61 | fn valid_asrt_basic() { 62 | assert!(1 > 0) 63 | } 64 | 65 | // Should be invalid 66 | #[condition(pre="true", post="true")] 67 | fn invalid_asrt_basic() { 68 | assert!(1 < 0) 69 | } 70 | 71 | // FIXME ERROR This test fails to compile. 72 | // Should be valid 73 | //#[condition(pre="x:bool == true", post="true")] 74 | fn valid_asrt_argument(x: bool) { 75 | assert!(x) 76 | } 77 | 78 | // * * * 79 | // Conditional Branch Tests 80 | // * * * 81 | 82 | // Should be valid 83 | #[condition(pre="true", post="true")] 84 | fn valid_basic_literal_if() { 85 | if true { 86 | assert!(1 > 0) 87 | } else { 88 | assert!(1 < 0) 89 | } 90 | } 91 | 92 | //Matt's example. Should be invalid. Calling it valid 93 | #[condition(pre="true",post="true")] 94 | fn invalid_fake_function(){ 95 | if true { 96 | assert!(1<0) 97 | } else { 98 | assert!(1>0) 99 | } 100 | } 101 | 102 | // Should be invalid 103 | #[condition(pre="true", post="true")] 104 | fn invalid_basic_literal_if() { 105 | if true { 106 | assert!(1 < 0) 107 | } else { 108 | assert!(1 > 0) 109 | } 110 | } 111 | 112 | // Should be valid 113 | #[condition(pre="true", post="true")] 114 | fn valid_large_branch_literal_if() { 115 | if false { 116 | assert!(1 < 0) 117 | } else if true { 118 | assert!(1 > 0) 119 | } else { 120 | assert!(1 < 0) 121 | } 122 | } 123 | 124 | // Should be invalid 125 | #[condition(pre="true", post="true")] 126 | fn invalid_large_branch_literal_if() { 127 | if false { 128 | assert!(1 < 0) 129 | } else if false { 130 | assert!(1 > 0) 131 | } else { 132 | assert!(1 < 0) 133 | } 134 | } 135 | 136 | // * * * 137 | // Boolean Tests 138 | // * * * 139 | 140 | // Should be valid 141 | #[condition(pre="x:bool == true", post="true")] 142 | fn valid_boolean_comparison_in_condition(x:bool) -> bool { 143 | x 144 | } 145 | 146 | // Should be invalid 147 | #[condition(pre="x:bool == true", post="false")] 148 | fn invalid_boolean_comparison_in_condition(x:bool) -> bool { 149 | x 150 | } 151 | 152 | // Should be valid 153 | #[condition(pre="true", post="return:bool == true")] 154 | fn valid_simple_bool(x:bool) -> bool { 155 | x || true 156 | } 157 | 158 | // Should be valid 159 | #[condition(pre="x:bool == true", post="return:bool == true")] 160 | fn valid_boolean_condition(x:bool) -> bool { 161 | if x { 162 | true 163 | } else { 164 | false 165 | } 166 | } 167 | 168 | // Should be invalid 169 | #[condition(pre="x:bool == false", post="return:bool == true")] 170 | fn invalid_boolean_condition(x:bool) -> bool { 171 | if x { 172 | true 173 | } else { 174 | false 175 | } 176 | } 177 | 178 | -------------------------------------------------------------------------------- /tests/test_fail_invalid.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, custom_attribute)] 2 | #![plugin(rustproof)] 3 | #![allow(dead_code)] 4 | #![allow(unused_attributes)] 5 | fn main() { } 6 | 7 | // Used to test the tester 8 | 9 | // Condition is valid, and function name indicates "invalid", so should be detected as a mismatch 10 | #[condition(pre="x: i32 <= i32::MAX - 5i32", post="return: i32 == (x: i32 + 5i32)")] 11 | fn invalid_add_five_i32(x: i32) -> i32 { 12 | x+5 13 | } 14 | -------------------------------------------------------------------------------- /tests/test_fail_valid.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, custom_attribute)] 2 | #![plugin(rustproof)] 3 | #![allow(dead_code)] 4 | #![allow(unused_attributes)] 5 | fn main() { } 6 | 7 | // Used to test the tester 8 | 9 | // Condition is invalid, and function name indicates "valid", so should be detected as a mismatch 10 | #[condition(pre="x: i32 <= i32::MAX - 4i32", post="return: i32 == (x: i32 + 5i32)")] 11 | fn valid_add_five_i32(x: i32) -> i32 { 12 | x+5 13 | } 14 | -------------------------------------------------------------------------------- /tests/test_signed.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, custom_attribute)] 2 | #![plugin(rustproof)] 3 | #![allow(dead_code)] 4 | #![allow(unused_attributes)] 5 | fn main() { } 6 | 7 | // * * * 8 | // Signed Integer Operator Tests 9 | // * * * 10 | 11 | // Tests signed integer for 32 bit integers 12 | // Should be invalid 13 | #[condition(pre="(x: i32 <= 10i32) && (x: i32 >= 0i32)", post="return: i32 > 5i32")] 14 | fn invalid_add_five_i32(x: i32) -> i32 { 15 | x + 5 16 | } 17 | 18 | // Tests signed integer for 32 bit integers 19 | // Should be valid 20 | #[condition(pre="(x: i32 <= 10i32) && (x: i32 >= 0i32)", post="return: i32 > 4i32")] 21 | fn valid_add_five_i32(x: i32) -> i32 { 22 | x + 5 23 | } 24 | 25 | // Tests signed integer for 32 bit integers 26 | // Should be invalid 27 | #[condition(pre="(x: i32 < 0i32) && (x: i32 >= -10i32)", post="return: i32 > 0i32")] 28 | fn invalid_add_five_negative_i32(x: i32) -> i32 { 29 | x + 5 30 | } 31 | 32 | // Tests signed integer for 32 bit integers 33 | // Should be valid 34 | #[condition(pre="(x: i32 < 0i32) && (x: i32 >= -10i32)", post="return: i32 > -6i32")] 35 | fn valid_add_five_negative_i32(x: i32) -> i32 { 36 | x + 5 37 | } 38 | 39 | // Tests signed integer for 32 bit integers 40 | // Should be invalid 41 | #[condition(pre="(x: i32 <= 10i32) && (x: i32 >= 0i32)", post="return: i32 > 4i32")] 42 | fn invalid_subtract_five_i32(x: i32) -> i32 { 43 | x - 5 44 | } 45 | 46 | // Tests signed integer for 32 bit integers 47 | // Should be valid 48 | #[condition(pre="(x: i32 <= 10i32) && (x: i32 >= 0i32)", post="return: i32 < 6i32")] 49 | fn valid_subtract_five_i32(x: i32) -> i32 { 50 | x - 5 51 | } 52 | 53 | // Tests signed integer for 32 bit integers 54 | // Should be invalid 55 | #[condition(pre="(x: i32 < 0i32) && (x: i32 >= -10i32)", post="return: i32 > 0i32")] 56 | fn invalid_subtract_five_negative_i32(x: i32) -> i32 { 57 | x - 5 58 | } 59 | 60 | // Tests signed integer for 32 bit integers 61 | // Should be valid 62 | #[condition(pre="(x: i32 < 0i32) && (x: i32 >= -10i32)", post="return: i32 < -2i32")] 63 | fn valid_subtract_five_negative_i32(x: i32) -> i32 { 64 | x - 5 65 | } 66 | 67 | // Tests signed integer for 32 bit integers 68 | // Should be invalid 69 | #[condition(pre="(x: i32 <= 10i32) && (x: i32 >= 0i32)", post="return: i32 < 0i32")] 70 | fn invalid_multiply_five_i32(x: i32) -> i32 { 71 | x * 5i32 72 | } 73 | 74 | // Tests signed integer for 32 bit integers 75 | // Should be valid 76 | #[condition(pre="(x: i32 <= 10i32) && (x: i32 >= 0i32)", post="return: i32 >= 0i32")] 77 | fn valid_multiply_five_i32(x: i32) -> i32 { 78 | x * 5 79 | } 80 | 81 | // Tests signed integer for 32 bit integers 82 | // Should be invalid 83 | #[condition(pre="(x: i32 < 0i32) && (x: i32 >= -10i32)", post="return: i32 == -2i32")] 84 | fn invalid_multiply_five_negative_i32(x: i32) -> i32 { 85 | x * 5 86 | } 87 | 88 | // Tests signed integer for 32 bit integers 89 | // Should be valid 90 | #[condition(pre="(x: i32 < 0i32) && (x: i32 >= -10i32)", post="return: i32 < 0i32")] 91 | fn valid_multiply_five_negative_i32(x: i32) -> i32 { 92 | x * 5 93 | } 94 | 95 | // Tests signed integer for 32 bit integers 96 | // Should be invalid 97 | #[condition(pre="(x: i32 >= 0i32) && (x: i32 <= 10i32)", post="return: i32 >= 0i32")] 98 | fn invalid_divide_five_i32(x: i32) -> i32 { 99 | 5 / x 100 | } 101 | 102 | 103 | // Tests signed integer for 32 bit integers 104 | // Should be valid 105 | #[condition(pre="(x: i32 > 0i32) && (x: i32 <= 10i32)", post="return: i32 >= 0i32")] 106 | fn valid_divide_five_i32(x: i32) -> i32 { 107 | 2 / x 108 | } 109 | 110 | // Tests signed integer for 32 bit integers 111 | // Should be invalid 112 | #[condition(pre="(x: i32 < 0i32) && (x: i32 > -10i32)", post="return: i32 > 1i32")] 113 | fn invalid_divide_five_negative_i32(x: i32) -> i32 { 114 | x / 2 115 | } 116 | 117 | // Tests signed integer for 32 bit integers 118 | // Should be valid 119 | #[condition(pre="(x: i32 < 0i32) && (x: i32 > -10i32)", post="return: i32 < 1i32")] 120 | fn valid_divide_five_negative_i32(x: i32) -> i32 { 121 | x / 2 122 | } 123 | 124 | // Tests signed integer for 32 bit integers 125 | // Should be invalid 126 | #[condition(pre="(x: i32 >= 0i32) && (x: i32 <= 10i32)", post="return: i32 < 0i32")] 127 | fn invalid_mod_five_i32(x: i32) -> i32 { 128 | x % 5 129 | } 130 | 131 | // Tests signed integer for 32 bit integers 132 | // Should be valid 133 | #[condition(pre="(x: i32 > 0i32) && (x: i32 <= 10i32)", post="return: i32 >= 0i32")] 134 | fn valid_mod_five_i32(x: i32) -> i32 { 135 | x % 5 136 | } 137 | 138 | // Tests signed integer for 32 bit integers 139 | // Should be invalid 140 | #[condition(pre="(x: i32 < 0i32) && (x: i32 > -10i32)", post="return: i32 > 1i32")] 141 | fn invalid_mod_five_negative_i32(x: i32) -> i32 { 142 | x % 5 143 | } 144 | 145 | // Tests signed integer for 32 bit integers 146 | // Should be valid 147 | #[condition(pre="(x: i32 < 0i32) && (x: i32 > -10i32)", post="return: i32 < 10i32")] 148 | fn valid_mod_five_negative_i32(x: i32) -> i32 { 149 | x % 5 150 | } 151 | 152 | // Tests signed integer for 32 bit integers 153 | // Should be invalid 154 | #[condition(pre="(x: i32 > 0i32) && (x: i32 < 10i32)", post="return: i32 > 10i32")] 155 | fn invalid_unary_minus_five_i32(x: i32) -> i32 { 156 | x + (-(5)) 157 | } 158 | 159 | // Tests signed integer for 32 bit integers 160 | // Should be valid 161 | #[condition(pre="(x: i32 > 0i32) && (x: i32 < 10i32)", post="return: i32 < 10i32")] 162 | fn valid_unary_minus_five_i32(x: i32) -> i32 { 163 | x + (-(5)) 164 | } 165 | 166 | // Tests signed integer for 32 bit integers 167 | // Should be invalid 168 | #[condition(pre="(x: i32 < 0i32) && (x: i32 > -10i32)", post="return: i32 > 0i32")] 169 | fn invalid_unary_minus_negative_five_i32(x: i32) -> i32 { 170 | x + (-(5)) 171 | } 172 | 173 | // Tests signed integer for 32 bit integers 174 | // Should be valid 175 | #[condition(pre="(x: i32 < 0i32) && (x: i32 > -10i32)", post="return: i32 < 0i32")] 176 | fn valid_unary_minus_five_negative_i32(x: i32) -> i32 { 177 | x + (-(5)) 178 | } 179 | 180 | // Tests signed integer for 32 bit integers 181 | // Should be invalid 182 | #[condition(pre="(x: i32 > 0i32) && (x: i32 < 10i32)", post="return: i32 < 0i32")] 183 | fn invalid_bit_shift_left_two_i32(x: i32) -> i32 { 184 | x << 2i32 185 | } 186 | 187 | // Tests signed integer for 32 bit integers 188 | // Should be valid 189 | #[condition(pre="(x: i32 > 0i32) && (x: i32 < 10i32)", post="return: i32 > 0i32")] 190 | fn valid_bit_shift_left_two_i32(x: i32) -> i32 { 191 | x << 2i32 192 | } 193 | 194 | // Tests signed integer for 32 bit integers 195 | // Should be invalid 196 | #[condition(pre="(x: i32 < 0i32) && (x: i32 > -10i32)", post="return: i32 > 0i32")] 197 | fn invalid_bit_shift_left_two_negative_i32(x: i32) -> i32 { 198 | x << 2i32 199 | } 200 | 201 | // Tests signed integer for 32 bit integers 202 | // Should be valid 203 | #[condition(pre="(x: i32 < 0i32) && (x: i32 > -10i32)", post="return: i32 < 2i32")] 204 | fn valid_bit_shift_left_two_negative_i32(x: i32) -> i32 { 205 | x << 2i32 206 | } 207 | 208 | 209 | // Tests signed integer for 32 bit integers 210 | // Should be invalid 211 | #[condition(pre="(x: i32 > 0i32) && (x: i32 < 10i32)", post="return: i32 > 0i32")] 212 | fn invalid_bit_shift_right_two_i32(x: i32) -> i32 { 213 | x >> 2i32 214 | } 215 | 216 | // Tests signed integer for 32 bit integers 217 | // Should be valid 218 | #[condition(pre="(x: i32 > 0i32) && (x: i32 < 10i32)", post="return: i32 >= 0i32")] 219 | fn valid_bit_shift_right_two_i32(x: i32) -> i32 { 220 | x >> 2i32 221 | } 222 | 223 | // Tests signed integer for 32 bit integers 224 | // Should be invalid 225 | #[condition(pre="(x: i32 < 0i32) && (x: i32 > -10i32)", post="return: i32 > 0i32")] 226 | fn invalid_bit_shift_right_negative_two_i32(x: i32) -> i32 { 227 | x >> 2i32 228 | } 229 | 230 | // Tests signed integer for 32 bit integers 231 | // Should be valid 232 | #[condition(pre="(x: i32 < 0i32) && (x: i32 > -10i32)", post="return: i32 <= 0i32")] 233 | fn valid_bit_shift_right_two_negative_i32(x: i32) -> i32 { 234 | x >> 2i32 235 | } 236 | 237 | // Tests signed integer for 16 bit integers 238 | // Should be valid 239 | #[condition(pre="(x: i8 < 10i8) && (x: i8 > 0i8)", post="return: i8 > 3i8")] 240 | fn valid_add_5_i8(x: i8) -> i8 { 241 | x+5i8 242 | } 243 | 244 | 245 | // Tests signed integer for 16 bit integers 246 | // Should be valid 247 | #[condition(pre="(x: i16 < 10i16) && (x: i16 > 0i16)", post="return: i16 > 3i16")] 248 | fn valid_add_5_i16(x: i16) -> i16 { 249 | x+5 250 | } 251 | 252 | // Tests signed integer for 64 bit integers 253 | // Should be valid 254 | #[condition(pre="(x: i64 < 10i64) && (x: i64 > 0i64)", post="return: i64 > 3i64")] 255 | fn valid_add_5_i64(x: i64) -> i64 { 256 | x+5 257 | } 258 | 259 | // Overflow Tests 260 | 261 | // Tests signed integer for 32 bit integers 262 | // Should be invalid 263 | #[condition(pre="(x: i32 == i32::MAX)", post="return: i32 > 0i32")] 264 | fn invalid_add_five_overflow_i32(x: i32) -> i32 { 265 | x + 5 266 | } 267 | 268 | // Tests signed integer for 32 bit integers 269 | // Should be invalid 270 | #[condition(pre="(x: i32 == i32::MIN)", post="return: i32 < 0i32")] 271 | fn invalid_subtract_five_overflow_i32(x: i32) -> i32 { 272 | x - 5 273 | } 274 | 275 | // Tests signed integer for 32 bit integers 276 | // Should be invalid 277 | #[condition(pre="(x: i32 == i32::MIN)", post="return: i32 > 0i32")] 278 | fn invalid_divide_one_overflow_i32(x: i32) -> i32 { 279 | x / -1 280 | } 281 | 282 | // Tests signed integer for 32 bit integers 283 | // Should be invalid 284 | #[condition(pre="(x: i32 == i32::MIN)", post="return: i32 > 0i32")] 285 | fn invalid_mod_one_overflow_i32(x: i32) -> i32 { 286 | x % -1 287 | } 288 | 289 | // Tests signed integer for 32 bit integers 290 | // Should be invalid 291 | #[condition(pre="(x: i32 == i32::MAX)", post="return: i32 > 0i32")] 292 | fn invalid_multiply_five_overflow_i32(x: i32) -> i32 { 293 | x * 5 294 | } 295 | 296 | // Tests signed integer for 16 bit integers 297 | // Should be invalid 298 | #[condition(pre="(x: i16 == i16::MAX)", post="return: i16 > 0i16")] 299 | fn invalid_add_five_overflow_i16(x: i16) -> i16 { 300 | x + 5 301 | } 302 | 303 | // Tests signed integer for 16 bit integers 304 | // Should be invalid 305 | #[condition(pre="(x: i16 == i16::MIN)", post="return: i16 < 0i16")] 306 | fn invalid_subtract_five_overflow_i16(x: i16) -> i16 { 307 | x - 5 308 | } 309 | 310 | // Tests signed integer for 8 bit integers 311 | // Should be invalid 312 | #[condition(pre="(x: i8 == i8::MAX)", post="return: i8 > 0i8")] 313 | fn invalid_add_five_overflow_i8(x: i8) -> i8 { 314 | x + 5 315 | } 316 | 317 | // Tests signed integer for 8 bit integers 318 | // Should be invalid 319 | #[condition(pre="(x: i8 == i8::MIN)", post="return: i8 < 0i8")] 320 | fn invalid_subtract_five_overflow_i8(x: i8) -> i8 { 321 | x - 5 322 | } 323 | 324 | // Tests signed integer for 64 bit integers 325 | // Should be invalid 326 | #[condition(pre="(x: i64 == i64::MAX)", post="return: i64 > 0i64")] 327 | fn invalid_add_five_overflow_i64(x: i64) -> i64 { 328 | x + 5 329 | } 330 | 331 | // Tests signed integer for 64 bit integers 332 | // Should be invalid 333 | #[condition(pre="(x: i64 == i64::MIN)", post="return: i64 < 0i64")] 334 | fn invalid_subtract_five_overflow_i64(x: i64) -> i64 { 335 | x - 5 336 | } 337 | -------------------------------------------------------------------------------- /tests/test_unsigned.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin, custom_attribute)] 2 | #![plugin(rustproof)] 3 | #![allow(dead_code)] 4 | #![allow(unused_attributes)] 5 | fn main() { } 6 | 7 | // * * * 8 | // Unsigned Integer Operator Tests 9 | // * * * 10 | 11 | // Tests unsigned integer for 32 bit integers 12 | // Should be invalid 13 | #[condition(pre="(x: u32 <= 10u32) && (x: u32 >= 0u32)", post="return: u32 > 5u32")] 14 | fn invalid_add_five_u32(x: u32) -> u32 { 15 | x + 5 16 | } 17 | 18 | // Tests unsigned integer for 32 bit integers 19 | // Should be valid 20 | #[condition(pre="(x: u32 <= 10u32) && (x: u32 >= 0u32)", post="return: u32 > 4u32")] 21 | fn valid_add_five_u32(x: u32) -> u32 { 22 | x + 5 23 | } 24 | 25 | // Tests unsigned integer for 32 bit integers 26 | // Should be invalid 27 | #[condition(pre="(x: u32 <= 10u32) && (x: u32 >= 0u32)", post="return: u32 > 10u32")] 28 | fn invalid_subtract_five_u32(x: u32) -> u32 { 29 | x - 5 30 | } 31 | 32 | // Tests unsigned integer for 32 bit integers 33 | // Should be valid 34 | #[condition(pre="(x: u32 <= 10u32) && (x: u32 > 5u32)", post="return: u32 < 6u32")] 35 | fn valid_subtract_five_u32(x: u32) -> u32 { 36 | x - 5 37 | } 38 | 39 | // Test to check unsigned subtration underflow 40 | // Should be valid 41 | #[condition(pre="x:u64 == 7u64", post="return:u64 == 5u64")] 42 | fn valid_subtraction_underflow(x:u64) -> u64 { 43 | return x-2; 44 | } 45 | 46 | // Tests unsigned integer for 32 bit integers 47 | // Should be invalid 48 | #[condition(pre="(x: u32 <= 10u32) && (x: u32 >= 0u32)", post="return: u32 < 0u32")] 49 | fn invalid_multiply_five_u32(x: u32) -> u32 { 50 | x * 5u32 51 | } 52 | 53 | // Tests unsigned integer for 32 bit integers 54 | // Should be valid 55 | #[condition(pre="(x: u32 <= 10u32) && (x: u32 >= 0u32)", post="return: u32 >= 0u32")] 56 | fn valid_multiply_five_u32(x: u32) -> u32 { 57 | x * 5 58 | } 59 | 60 | // Tests unsigned integer for 32 bit integers 61 | // Should be invalid 62 | #[condition(pre="(x: u32 >= 0u32) && (x: u32 <= 10u32)", post="return: u32 >= 0u32")] 63 | fn invalid_divide_five_u32(x: u32) -> u32 { 64 | 5 / x 65 | } 66 | 67 | // Tests unsigned integer for 32 bit integers 68 | // Should be valid 69 | #[condition(pre="(x: u32 > 0u32) && (x: u32 <= 10u32)", post="return: u32 >= 0u32")] 70 | fn valid_divide_five_u32(x: u32) -> u32 { 71 | 2 / x 72 | } 73 | 74 | // Tests unsigned integer for 32 bit integers 75 | // Should be invalid 76 | #[condition(pre="(x: u32 >= 0u32) && (x: u32 <= 10u32)", post="return: u32 < 0u32")] 77 | fn invalid_mod_five_u32(x: u32) -> u32 { 78 | x % 5 79 | } 80 | 81 | // Tests unsigned integer for 32 bit integers 82 | // Should be valid 83 | #[condition(pre="(x: u32 > 0u32) && (x: u32 <= 10u32)", post="return: u32 >= 0u32")] 84 | fn valid_mod_five_u32(x: u32) -> u32 { 85 | x % 5 86 | } 87 | 88 | // Tests unsigned integer for 32 bit integers 89 | // Should be invalid 90 | #[condition(pre="(x: u32 > 0u32) && (x: u32 < 10u32)", post="return: u32 < 0u32")] 91 | fn invalid_bit_shift_left_two_u32(x: u32) -> u32 { 92 | x << 2u32 93 | } 94 | 95 | // Tests unsigned integer for 32 bit integers 96 | // Should be valid 97 | #[condition(pre="(x: u32 > 0u32) && (x: u32 < 10u32)", post="return: u32 > 0u32")] 98 | fn valid_bit_shift_left_two_u32(x: u32) -> u32 { 99 | x << 2u32 100 | } 101 | 102 | // Tests unsigned integer for 32 bit integers 103 | // Should be invalid 104 | #[condition(pre="(x: u32 > 0u32) && (x: u32 < 10u32)", post="return: u32 > 0u32")] 105 | fn invalid_bit_shift_right_two_u32(x: u32) -> u32 { 106 | x >> 2u32 107 | } 108 | 109 | // Tests unsigned integer for 32 bit integers 110 | // Should be valid 111 | #[condition(pre="(x: u32 > 0u32) && (x: u32 < 10u32)", post="return: u32 >= 0u32")] 112 | fn valid_bit_shift_right_two_u32(x: u32) -> u32 { 113 | x >> 2u32 114 | } 115 | 116 | // Tests unsigned integer for 16 bit integers 117 | // Should be valid 118 | #[condition(pre="(x: u8 < 10u8) && (x: u8 > 0u8)", post="return: u8 > 3u8")] 119 | fn valid_add_5_u8(x: u8) -> u8 { 120 | x+5u8 121 | } 122 | 123 | // Tests unsigned integer for 16 bit integers 124 | // Should be valid 125 | #[condition(pre="(x: u16 < 10u16) && (x: u16 > 0u16)", post="return: u16 > 3u16")] 126 | fn valid_add_5_u16(x: u16) -> u16 { 127 | x+5 128 | } 129 | 130 | // Tests unsigned integer for 64 bit integers 131 | // Should be valid 132 | #[condition(pre="(x: u64 < 10u64) && (x: u64 > 0u64)", post="return: u64 > 3u64")] 133 | fn valid_add_5_u64(x: u64) -> u64 { 134 | x+5 135 | } 136 | 137 | // Tests signed integer for 32 bit integers 138 | // Should be invalid 139 | #[condition(pre="(x: u32 == u32::MAX)", post="return: u32 > 20u32")] 140 | fn invalid_add_five_overflow_u32(x: u32) -> u32 { 141 | x + 5 142 | } 143 | 144 | // Tests signed integer for 32 bit integers 145 | // Should be invalid 146 | #[condition(pre="(x: u32 == u32::MIN)", post="return: u32 < 20u32")] 147 | fn invalid_subtract_five_overflow_u32(x: u32) -> u32 { 148 | x - 5 149 | } 150 | 151 | // Tests signed integer for 32 bit integers 152 | // Should be invalid 153 | #[condition(pre="(x: u32 == u32::MAX)", post="true")] 154 | fn invalid_multiply_two_overflow_u32(x: u32) -> u32 { 155 | x * 2 156 | } 157 | 158 | // Tests signed integer for 16 bit integers 159 | // Should be invalid 160 | #[condition(pre="(x: u16 == u16::MAX)", post="return: u16 > 20u16")] 161 | fn invalid_add_five_overflow_u16(x: u16) -> u16 { 162 | x + 5 163 | } 164 | 165 | // Tests signed integer for 16 bit integers 166 | // Should be invalid 167 | #[condition(pre="(x: u16 == u16::MIN)", post="return: u16 < 20u16")] 168 | fn invalid_subtract_five_overflow_u16(x: u16) -> u16 { 169 | x - 5 170 | } 171 | 172 | // Tests signed integer for 8 bit integers 173 | // Should be invalid 174 | #[condition(pre="(x: u8 == u8::MAX)", post="return: u8 > 20u8")] 175 | fn invalid_add_five_overflow_u8(x: u8) -> u8 { 176 | x + 5 177 | } 178 | 179 | // Tests signed integer for 8 bit integers 180 | // Should be invalid 181 | #[condition(pre="(x: u8 == u8::MIN)", post="return: u8 < 20u8")] 182 | fn invalid_subtract_five_overflow_u8(x: u8) -> u8 { 183 | x - 5 184 | } 185 | 186 | // Tests signed integer for 64 bit integers 187 | // Should be invalid 188 | #[condition(pre="(x: u64 == u64::MAX)", post="return: u64 > 20u64")] 189 | fn invalid_add_five_overflow_u64(x: u64) -> u64 { 190 | x + 5 191 | } 192 | 193 | // Tests signed integer for 64 bit integers 194 | // Should be invalid 195 | #[condition(pre="(x: u64 == u64::MIN)", post="return: u64 < 20u64")] 196 | fn invalid_subtract_five_overflow_u64(x: u64) -> u64 { 197 | x - 5 198 | } 199 | --------------------------------------------------------------------------------