Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Runtime checks #1452

Draft
wants to merge 49 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
01923a9
Override built_mir method to have mutable access to mir
cedihegi Apr 12, 2023
613db71
Change a single assertion to test output
cedihegi May 1, 2023
531842a
Add runtime checks for preconditions and insert them in MIR
cedihegi May 3, 2023
9dc1072
Fix the problem of introducing loops in CFG when modifying it.
cedihegi May 5, 2023
d4cbd89
Check postconditions too and handle result
cedihegi May 8, 2023
2ecdda6
Handle old operator in runtime checks
cedihegi May 11, 2023
393748c
Make postconditions with old expressions checkable at runtime
cedihegi May 16, 2023
00d47fc
Runtime check support for quantifiers, old stores and postconditions
cedihegi May 29, 2023
61355c2
Add option, to provide boundaries for quantifier runtime checks manually
cedihegi May 31, 2023
a4ce18b
Fix bug with mem forget
cedihegi Jun 3, 2023
8001fe5
Interpret moved values in postconditions as old values too
cedihegi Jun 5, 2023
4759f3b
Now able to insert runtime checks for pledges.
cedihegi Jun 13, 2023
0351d45
Add runtime checks for pledges
cedihegi Jun 14, 2023
fafabe0
Fix a bug for runtime checks
cedihegi Jun 20, 2023
96807d6
Runtime checks for prusti_assert and prusti_assume
cedihegi Jun 24, 2023
823e766
Working old checks in prusti_assume, assert etc.
cedihegi Jul 11, 2023
107ef3b
Re-organized runtime check structure a bit, remove warnings
cedihegi Jul 12, 2023
17b73d7
Only clone arguments for old if they are mutable
cedihegi Jul 19, 2023
3c82745
Runtime checks without store function for old values
cedihegi Jul 26, 2023
ec781ae
Fix check signature mutability issues with runtime checks
cedihegi Jul 26, 2023
a6daab3
Insert drops for manually cloned MIR locals
cedihegi Jul 30, 2023
ddcf67c
Merge remote-tracking branch 'upstream/master' into runtime-checks
cedihegi Aug 1, 2023
a3b88e0
Refactor runtime checks
cedihegi Aug 5, 2023
b276862
Add boolean guards for pledge checks
cedihegi Aug 6, 2023
6f089cd
Optmization based on verification and reachability
cedihegi Aug 9, 2023
4bff3d6
Pledges without annotations and Dead Code Eliminiation
cedihegi Aug 16, 2023
f3ec99b
Fix pledge checks in case of zombie loans
cedihegi Aug 17, 2023
98c0317
Remove unneeded assertions and checked operations
cedihegi Aug 20, 2023
066ecc5
cleanup changes that are no longer required
cedihegi Aug 20, 2023
bcbb303
Undo accidental formatting
cedihegi Aug 21, 2023
a4891c9
syncing! override by amending!
cedihegi Aug 22, 2023
8300225
merging with master
cedihegi Aug 25, 2023
351e9fa
Fix tests, add option to always insert runtime checks
cedihegi Aug 26, 2023
be5482c
Add flags for runtime checks debugging
cedihegi Aug 28, 2023
294884b
Extend error reporting of runtime checks
cedihegi Aug 30, 2023
d3cacc7
Add tests for runtime checks and optimizations
cedihegi Sep 1, 2023
4a0f5b6
Add better error messages to runtime checks
cedihegi Sep 2, 2023
de67aa4
Run formatting
cedihegi Sep 6, 2023
2fbeed9
Refactoring and resolving clippy warnings
cedihegi Sep 6, 2023
f4aa968
Stop std feature from importing prusti-specs
cedihegi Sep 10, 2023
04e66dd
Merge remote-tracking branch 'upstream/master' into runtime-checks
cedihegi Sep 10, 2023
ae80faa
Document, refactor and explain with comments
cedihegi Sep 11, 2023
bd3b3dd
Add runtime checks for predicates
cedihegi Sep 12, 2023
5c05f2e
More comments, documentation and a test for predicate
cedihegi Sep 13, 2023
2e8fb1e
Update predicates and predicate checking + other things
cedihegi Sep 18, 2023
2f5d8e6
Add new flags to dev-guide
cedihegi Sep 20, 2023
5cfb19a
Merge remote-tracking branch 'upstream/master' into runtime-checks
cedihegi Sep 22, 2023
2c92e1a
Remove prints, add comments, slight adjustment of old_values
cedihegi Oct 9, 2023
6279743
Small changes and fixes
cedihegi Oct 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 248 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions docs/dev-guide/src/config/flags.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
| [`CHECK_PANICS`](#check_panics) | `bool` | `true` | A |
| [`CHECK_TIMEOUT`](#check_timeout) | `Option<u32>` | `None` | A |
| [`COUNTEREXAMPLE`](#counterexample) | `bool` | `false` | A |
| [`DEBUG_RUNTIME_CHECKS`](#debug_runtime_checks) | `bool` | `false` | A |
| [`DELETE_BASIC_BLOCKS`](#delete_basic_blocks) | `Vec<String>` | `vec![]` | A |
| [`DISABLE_NAME_MANGLING`](#disable_name_mangling) | `bool` | `false` | A |
| [`DUMP_BORROWCK_INFO`](#dump_borrowck_info) | `bool` | `false` | A |
Expand All @@ -34,6 +35,7 @@
| [`FULL_COMPILATION`](#full_compilation) | `bool` | `false` | A* |
| [`HIDE_UUIDS`](#hide_uuids) | `bool` | `false` | A |
| [`IGNORE_REGIONS`](#ignore_regions) | `bool` | `false` | A |
| [`INSERT_RUNTIME_CHECKS`](#insert_runtime_checks) | `string` | `"none"` | A |
| [`INTERNAL_ERRORS_AS_WARNINGS`](#internal_errors_as_warnings) | `bool` | `false` | A |
| [`INTERN_NAMES`](#intern_names) | `bool` | `true` | A |
| [`JAVA_HOME`](#java_home) | `Option<String>` | `None` | A |
Expand All @@ -56,6 +58,7 @@
| [`PRINT_HASH`](#print_hash) | `bool` | `false` | A |
| [`PRINT_TYPECKD_SPECS`](#print_typeckd_specs) | `bool` | `false` | A |
| [`QUIET`](#quiet) | `bool` | `false` | A* |
| [`REMOVE_DEAD_CODE`](#remove_dead_code) | `bool` | `false` | A |
| [`SERVER_ADDRESS`](#server_address) | `Option<String>` | `None` | A |
| [`SERVER_MAX_CONCURRENCY`](#server_max_concurrency) | `Option<usize>` | `None` | A |
| [`SERVER_MAX_STORED_VERIFIERS`](#server_max_stored_verifiers) | `Option<usize>` | `None` | A |
Expand All @@ -81,6 +84,7 @@
| [`VIPER_HOME`](#viper_home) | `Option<String>` | `None` | A |
| [`WRITE_SMT_STATISTICS`](#write_smt_statistics) | `bool` | `false` | A |


## `ALLOW_UNREACHABLE_UNSUPPORTED_CODE`

When enabled, unsupported code is encoded as `assert false`. This way error messages are reported only for unsupported code that is actually reachable.
Expand Down Expand Up @@ -142,6 +146,10 @@ For more information see [here]( https://github.com/viperproject/silicon/blob/4c

When enabled, Prusti will try to find and print a counterexample for any failed assertion or specification.

## `DEBUG_RUNTIME_CHECKS`

When enabled, functions generated for runtime checking will emit a message when they are executed.

## `DELETE_BASIC_BLOCKS`

The given basic blocks will be replaced with `assume false`.
Expand Down Expand Up @@ -233,6 +241,15 @@ When enabled, UUIDs of expressions and specifications printed with [`PRINT_TYPEC

When enabled, debug files dumped by `rustc` will not contain lifetime regions.

## `INSERT_RUNTIME_CHECKS`

Whether or not to enable runtime checks. Accepts one of the following values:

- `"none"`: default option, no runtime checks will be inserted.
- `"selective"`: only contracts marked with `#[insert_runtime_check]` will be checked.
- `"all"`: all supported contracts will be runtime checked.


## `INTERNAL_ERRORS_AS_WARNINGS`

When enabled, internal errors are presented as warnings.
Expand Down Expand Up @@ -358,6 +375,10 @@ When enabled, user messages are not printed. Otherwise, messages output into `st

> **Note:** `cargo prusti` sets this flag with `DEFAULT_PRUSTI_QUIET=true`.

## `REMOVE_DEAD_CODE`

When enabled, Prusti will perform some optimizations on the generated executable by removing unreachable code and unneeded assertions and their corresponding checked operations.

## `SERVER_ADDRESS`

When set to an address and port (e.g. `"127.0.0.1:2468"`), Prusti will connect to the given server and use it for its verification backend.
Expand Down
3 changes: 3 additions & 0 deletions prusti-contracts/prusti-contracts-proc-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ proc-macro2 = { version = "1.0", optional = true }
# Are we being compiled by Prusti and should include dependency on
# prusti-specs and proc-macro2?
prusti = ["dep:prusti-specs", "dep:proc-macro2"]
# If std is active, and prusti-specs is imported, forward the std flag
# to prusti-specs.
std = ["prusti-specs?/std"]
28 changes: 28 additions & 0 deletions prusti-contracts/prusti-contracts-proc-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ pub fn body_variant(_tokens: TokenStream) -> TokenStream {
TokenStream::new()
}

#[cfg(not(feature = "prusti"))]
#[proc_macro_attribute]
pub fn quantifier_runtime_bounds(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
tokens
}

#[cfg(not(feature = "prusti"))]
#[proc_macro_attribute]
pub fn insert_runtime_check(_attr: TokenStream, _tokens: TokenStream) -> TokenStream {
TokenStream::new()
}

// ----------------------
// --- PRUSTI ENABLED ---

Expand Down Expand Up @@ -273,5 +285,21 @@ pub fn body_variant(tokens: TokenStream) -> TokenStream {
prusti_specs::body_variant(tokens.into()).into()
}

#[cfg(feature = "prusti")]
#[proc_macro_attribute]
pub fn quantifier_runtime_bounds(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
tokens
}

#[cfg(feature = "prusti")]
#[proc_macro_attribute]
pub fn insert_runtime_check(attr: TokenStream, tokens: TokenStream) -> TokenStream {
rewrite_prusti_attributes(
SpecAttributeKind::InsertRuntimeCheck,
attr.into(),
tokens.into(),
)
.into()
}
// Ensure that you've also crated a transparent `#[cfg(not(feature = "prusti"))]`
// version of your new macro above!
2 changes: 2 additions & 0 deletions prusti-contracts/prusti-contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ trybuild = "1.0"

# Forward "prusti" flag
[features]
default = ["std"]
prusti = ["prusti-contracts-proc-macros/prusti"]
std = ["prusti-contracts-proc-macros/std"]
14 changes: 13 additions & 1 deletion prusti-contracts/prusti-contracts/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![no_std]
#![cfg_attr(not(feature = "std"), no_std)]
// #![no_std]

/// A macro for writing a precondition on a function.
pub use prusti_contracts_proc_macros::requires;
Expand Down Expand Up @@ -66,6 +67,14 @@ pub use prusti_contracts_proc_macros::terminates;
/// A macro to annotate body variant of a loop to prove termination
pub use prusti_contracts_proc_macros::body_variant;

/// A macro to explicitly annotate a quantifier with ranges for their
/// runtime checks
pub use prusti_contracts_proc_macros::quantifier_runtime_bounds;

/// A macro to annotate contracts that should be checked
/// at runtime
pub use prusti_contracts_proc_macros::insert_runtime_check;

#[cfg(not(feature = "prusti"))]
mod private {
use core::marker::PhantomData;
Expand Down Expand Up @@ -124,6 +133,9 @@ mod private {
#[cfg(feature = "prusti")]
pub mod core_spec;

#[cfg(feature = "prusti")]
pub mod runtime_check_internals;

#[cfg(feature = "prusti")]
mod private {
use core::{marker::PhantomData, ops::*};
Expand Down
59 changes: 59 additions & 0 deletions prusti-contracts/prusti-contracts/src/runtime_check_internals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#[cfg(not(feature = "std"))]
use core::fmt::Write;

#[cfg(not(feature = "std"))]
struct SimpleFormatter<'a>(&'a mut [u8], usize);

#[cfg(not(feature = "std"))]
impl<'a> core::fmt::Write for SimpleFormatter<'a> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
let dest_len = self.0.len();
let copy_len = core::cmp::min(s.len(), dest_len);

self.0[..copy_len].copy_from_slice(&s.as_bytes()[..copy_len]);
self.1 = copy_len;

if s.len() > dest_len {
Err(core::fmt::Error)
} else {
Ok(())
}
}
}

#[cfg(not(feature = "std"))]
pub fn num_to_str<T: core::fmt::Display>(n: T, buffer: &mut [u8]) -> &str {
let mut formatter = SimpleFormatter(buffer, 0);
write!(&mut formatter, "{}", n).expect("Failed to write number to buffer");
let bytes_written = formatter.1;
core::str::from_utf8(&buffer[..bytes_written]).unwrap()
}

// An internal function used for getting more precise error messages for runtime
// checks.
// Buffer manipulations because we cannot use std and need to operate on a buffer
// that is allocated before this function is called.
#[cfg(not(feature = "std"))]
pub fn check_expr(expr: bool, added_info: &str, buffer: &mut [u8], buffer_len: &mut usize) -> bool {
if !expr {
// buffer already has contents up until buffer_len
let after_len = *buffer_len + added_info.len();
buffer[*buffer_len..after_len].copy_from_slice(added_info.as_bytes());
*buffer_len = after_len;
false
} else {
true
}
}

// If ctd is available, the check function can be a lot simpler, and doesn't
// need to operate on a passed buffer.
#[cfg(feature = "std")]
pub fn check_expr(expr: bool, added_info: &str, message: &mut std::string::String) -> bool {
if !expr {
message.push_str(added_info);
false
} else {
true
}
}
3 changes: 3 additions & 0 deletions prusti-contracts/prusti-specs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ proc-macro2 = "1.0"
uuid = { version = "1.0", features = ["v4"] }
itertools = "0.11"
rustc-hash = "1.1.0"

[features]
std = []
60 changes: 60 additions & 0 deletions prusti-contracts/prusti-specs/src/attribute_consumer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};

// By default, attributes can only be attached to items like functions or closures.
// The point of this is to allow attaching attributes to the contents of a
// prusti_assert!() or similar (predicate).
// Note: this kind of consuming is only necessary when we want to consume an
// attribute at the beginning of a TokenStream without fully parsing it (mainly
// because the rest still has to be parsed by prusti_parse)
pub(crate) struct AttributeConsumer {
pub attributes: Vec<syn::Attribute>,
pub rest: TokenStream,
}

impl Parse for AttributeConsumer {
fn parse(input: ParseStream) -> syn::Result<Self> {
let attributes = input.call(syn::Attribute::parse_outer)?;
let rest: TokenStream = input.parse().unwrap();
Ok(Self { attributes, rest })
}
}

// maybe we can make this more generic so it can be used in other places..
impl AttributeConsumer {
pub fn get_attribute(&mut self, name: &str) -> Option<syn::Attribute> {
if let Some(pos) = self.attributes.iter().position(|attr| {
if let Some(ident) = attr.path.get_ident() {
if *ident == name {
return true;
}
}
false
}) {
Some(self.attributes.remove(pos))
} else {
None
}
}

// /// A function that can be used to check that all attributes have been
// /// consumed. Returns an error with the span of the first remaining attribute
// pub fn check_no_remaining_attrs(&self) -> syn::Result<()> {
// if let Some(attr) = self.attributes.first() {
// Err(syn::Error::new(
// attr.span(),
// "This attribute could not be processed and probably doesn't belong here",
// ))
// } else {
// Ok(())
// }
// }

pub fn tokens(self) -> TokenStream {
let Self { attributes, rest } = self;
let mut attrs: TokenStream = attributes.into_iter().map(|attr| quote!(#attr)).collect();
attrs.extend(quote!(#rest));
attrs
}
}
30 changes: 30 additions & 0 deletions prusti-contracts/prusti-specs/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,3 +709,33 @@ mod tests {
}
}
}

pub enum RuntimeCheckMode {
All,
Selective,
Never,
}

impl RuntimeCheckMode {
pub fn determine() -> Self {
match std::env::var("PRUSTI_INSERT_RUNTIME_CHECKS")
.unwrap_or("never".to_string())
.as_str()
{
"all" => Self::All,
"selective" => Self::Selective,
"never" => Self::Never,
_ => Self::Never,
}
}

/// Depending on whether a contract has a #[insert_runtime_check] attribute
/// decide whether a check should actually be inserted
pub fn decide_insertion(&self, has_attr: bool) -> bool {
match (self, has_attr) {
(Self::All, _) => true,
(Self::Selective, b) => b,
(Self::Never, _) => false,
}
}
}
Loading