-
Notifications
You must be signed in to change notification settings - Fork 134
Introduce native kani / cargo-kani #738
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
Conversation
celinval
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took a quick look. It's a nice start! Thanks for doing this. Here are a few suggestions:
- I think it would be cleaner if we create two modules, one that does the external calls and one that does the parsing. You might as well split the objects too.
- Can you also add a bit more documentation to the files and functions?
src/cargo-kani/src/call_cargo.rs
Outdated
|
|
||
| use crate::context::KaniContext; | ||
|
|
||
| impl KaniContext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be inside KaniContext?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it's using self a lot. (Was even back when you asked, but perhaps only once then. Much more obvious now we have all the flags.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a general comment and I think we should address in a separate PR, but I would prefer breaking some of this logic down into more than one struct. I think it would be cleaner if we had smaller structs. We are using self a lot, and that could possible be broken down by calling other objects.
| /// Given a `file` (a .symtab.json), produce `{file}.out` by calling symtab2gb | ||
| pub fn link_c_lib(&self, inputs: &[PathBuf], output: &Path, function: &str) -> Result<()> { | ||
| { | ||
| let mut temps = self.temporaries.borrow_mut(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why borrow_mut? Shouldn't self be mutable? Or can we manage temporaries outside of KaniContext?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm making use of RefCell to let us pass around just a &self (not mut, so we don't need exclusivity), but then still update the list of temporaries.
src/cargo-kani/src/context.rs
Outdated
|
|
||
| pub fn kani_rustc(&self) -> Result<PathBuf> { | ||
| match self { | ||
| Self::DevRepo(repo) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you create a utility function? This seemed to be repeated in the functions bellow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(same as earlier comment: I'll fix it once we have another install type?)
src/cargo-kani/src/context.rs
Outdated
| } | ||
|
|
||
| /// Represents where we detected Kani, with helper methods for using that information to find critical paths | ||
| enum InstallType { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this? Can we use a build.rs instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the code to dynamic detect where all our dependencies are located at runtime. It looks weird right now because I'm not trying to support anything except "checked out repo, globally installed deps" except I was trying to still support a better structure later...
I'll refactor it later if you think we can tolerate it for now. As soon at we actually have another install type this should either become clear or get fixed...
|
|
||
| #[derive(Debug, StructOpt)] | ||
| pub struct CheckArgs { | ||
| // Rust argument parsers (/clap) don't have the convenient '--flag' and '--no-flag' boolean pairs, so approximate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently have two "modes": --default-checks where you write --no-blah-checks for specific categories. Or --no-default-checks where you write --blah-checks for specific categories. I'm supporting that for now. We could revisit eventually?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am in favor of revisiting. This seems to be adding unnecessary complexity and it doesn't seem to be a common practice in rust anyway.
|
|
||
| #[derive(Debug, StructOpt)] | ||
| pub struct CheckArgs { | ||
| // Rust argument parsers (/clap) don't have the convenient '--flag' and '--no-flag' boolean pairs, so approximate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently have two "modes": --default-checks where you write --no-blah-checks for specific categories. Or --no-default-checks where you write --blah-checks for specific categories. I'm supporting that for now. We could revisit eventually?
src/cargo-kani/src/call_cargo.rs
Outdated
|
|
||
| use crate::context::KaniContext; | ||
|
|
||
| impl KaniContext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it's using self a lot. (Was even back when you asked, but perhaps only once then. Much more obvious now we have all the flags.)
| /// Given a `file` (a .symtab.json), produce `{file}.out` by calling symtab2gb | ||
| pub fn link_c_lib(&self, inputs: &[PathBuf], output: &Path, function: &str) -> Result<()> { | ||
| { | ||
| let mut temps = self.temporaries.borrow_mut(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm making use of RefCell to let us pass around just a &self (not mut, so we don't need exclusivity), but then still update the list of temporaries.
src/cargo-kani/src/context.rs
Outdated
| } | ||
|
|
||
| /// Represents where we detected Kani, with helper methods for using that information to find critical paths | ||
| enum InstallType { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the code to dynamic detect where all our dependencies are located at runtime. It looks weird right now because I'm not trying to support anything except "checked out repo, globally installed deps" except I was trying to still support a better structure later...
I'll refactor it later if you think we can tolerate it for now. As soon at we actually have another install type this should either become clear or get fixed...
src/cargo-kani/src/context.rs
Outdated
|
|
||
| pub fn kani_rustc(&self) -> Result<PathBuf> { | ||
| match self { | ||
| Self::DevRepo(repo) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(same as earlier comment: I'll fix it once we have another install type?)
| [kani.flags.loudness] | ||
| quiet = false | ||
|
|
||
| [kani.flags.artifacts.extra.suffixes] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note for anyone reviewing: these things under kani.flags were pointless and I'm not sure why they were ever there. So I removed these "sections" that shouldn't be there.
Also, I removed --no-quiet and --no-visualize and mangler as options, so I removed those here too.
| .arg(&parent_dir); | ||
| .current_dir(&parent_dir); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the --crate flag in favor of just changing directories here.
| kani.args(&self.props.kani_flags) | ||
| .arg("--input") | ||
| .arg(&self.testpaths.file) | ||
| kani.arg(&self.testpaths.file) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the --input flag in favor of just passing the argument here.
celinval
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more comments. I'm OK leaving the comments related to polishing the code to a separate PR.
| /// Produces a set of arguments to pass to "self" (cargo-kani) from a Cargo.toml project file | ||
| pub fn config_toml_to_args() -> Result<Vec<OsString>> { | ||
| // TODO: `cargo locate-project` maybe? | ||
| let file = std::fs::read_to_string("Cargo.toml"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we need to handle --config-toml option?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing is using that flag, so I dropped it.
| ctx.run_goto_instrument(&linked_obj)?; | ||
|
|
||
| if ctx.args.visualize { | ||
| ctx.run_visualize(&linked_obj, "target/report")?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please use PathBuf?
| // TODO: currently only cargo-kani pays attention to this. | ||
| /// Directory for all generated artifacts | ||
| #[structopt(long, parse(from_os_str))] | ||
| pub target_dir: Option<PathBuf>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to set a default value here to avoid hard coding the default value in other places?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No: the default is different for standalone/cargo. The "hard coding in other places" is just the two different defaults.
I agree there's a design problem there, but it's not an obvious one to fix. I've deliberately avoided letting a KaniSession know if it's cargo-kani or kani because it's just supposed to represent the flags, but here's an area where it matters, and it gets a little annoying.
| // Common arguments for invoking Kani. This gets put into KaniContext, whereas | ||
| // anything above is "local" to "main"'s control flow. | ||
| #[derive(Debug, StructOpt)] | ||
| pub struct KaniArgs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the output for kani --help after this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now a big jumbled mess:
$ cargo-kani --help
cargo-kani 0.1.0
Verify a Rust crate. For more information, see https://github.com/model-checking/kani
USAGE:
cargo-kani [FLAGS] [OPTIONS]
FLAGS:
--allow-cbmc-verification-failure Do not produce error return code on CBMC verification failure
--assertion-reach-checks Turn on assertion reachability checks
--auto-unwind Turn on automatic loop unwinding
--debug Produce full debug information
--default-checks Turn on all default checks
--dry-run Print commands instead of running them
--gen-c Generate C file equivalent to inputted program
-h, --help Prints help information
--keep-temps Keep temporary files generated throughout Kani process
--memory-safety-checks Turn on default memory safety checks
--no-default-checks Turn off all default checks
--no-memory-safety-checks Turn off default memory safety checks
--no-overflow-checks Turn off default overflow checks
--no-restrict-vtable Disable restricting the targets of virtual table function pointer calls
--no-undefined-function-checks Turn off undefined function checks
--no-unwinding-checks Turn off default unwinding checks
--only-codegen Kani will only compile the crate
--overflow-checks Turn on default overflow checks
-q, --quiet Produces no output, just an exit code and requested artifacts; overrides
--verbose
--restrict-vtable Restrict the targets of virtual table function pointer calls
--tests Enable test function verification. Only use this option when the entry
point is a test function
--undefined-function-checks Turn on undefined function checks
--unwinding-checks Turn on default unwinding checks
--use-abs Use abstractions for the standard library
-V, --version Prints version information
-v, --verbose Output processing stages and commands, along with minor debug information
--visualize Generate visualizer report to <target-dir>/report/html/index.html
OPTIONS:
--abs-type <abs-type> Choose abstraction for modules of standard library if available [default:
std] [possible values: std, kani, c-ffi, no-back]
--c-lib <c-lib>... Link external C files referenced by Rust code
--cbmc-args <cbmc-args>... Pass through directly to CBMC; must be the last flag
--function <function> Entry point for verification [default: main]
--object-bits <object-bits> Specify the number of bits used for representing object IDs in CBMC [default:
16]
--output-format <output-format> Toggle between different styles of output [default: regular] [possible
values: Regular, Terse, Old]
--target-dir <target-dir> Directory for all generated artifacts
--unwind <unwind> Specify the value used for loop unwinding in CBMC
IMO, cleaning it up is another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. It uses the doc comments to generate the help information. It is clean but a bit error prone.
This is a topic for the next PR though. Thanks
| let mut temps = self.temporaries.borrow_mut(); | ||
| temps.push(output_filename.clone()); | ||
| temps.push(alter_extension(file, "type_map.json")); | ||
| temps.push(alter_extension(file, "kani-metadata.json")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we create a little utility class to generate these extensions for us? Another alternative is to create a constant for each type of extension. I noticed that they are hard-coded in different places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While they're hardcoded, they each appear exactly once (except "restrictions.json" which is a design flaw I might try to correct now...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Maybe once you merge this I can add a little utility module that can be used by kani / cargo-kani and the kani-compiler.
celinval
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I am OK signing off this PR the way it is since it is a clear improvement to what we have today. Thanks for that.
What's your plan to address the issues that we are leaving for follow up PRs though?
Some things like improving the --help output I think is high priority but I also see a lot of TODO statements that don't have any issue attached to them. There are also a lot of comments here that are going to be pushed to follow up PRs.
| // Common arguments for invoking Kani. This gets put into KaniContext, whereas | ||
| // anything above is "local" to "main"'s control flow. | ||
| #[derive(Debug, StructOpt)] | ||
| pub struct KaniArgs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. It uses the doc comments to generate the help information. It is clean but a bit error prone.
This is a topic for the next PR though. Thanks
| } | ||
|
|
||
| /// Translates one toml entry into arguments and inserts it into args | ||
| fn insert_arg_from_toml(flag: &str, value: &Value, args: &mut Vec<OsString>) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a question, what will happen if the user invokes cargo-kani with an argument in the command line that is also included in the Cargo.toml?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be the same thing as happens on the command line so:
$ cargo-kani --verbose --verbose
error: The argument '--verbose' was provided more than once, but cannot be used multiple times
USAGE:
cargo-kani --abs-type <abs-type> --function <function> --object-bits <object-bits> --output-format <output-format> --verbose
For more information try --help
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The command line argument should take precedence over the cargo configuration though. Like it does today. I believe this works today because the python implementation allows you to use an argument more than once.
On clap you can set the following setting AllArgsOverrideSelf to enable that behavior.
| print("Json File Input Missing") | ||
| sys.exit(1) | ||
|
|
||
| if len(sys.argv) == 3: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe @jaisnan can improve this logic a bit once you merge. I believe he only created the main in order to run some sanity checks.
|
I combed the PR and updated #701 with all follow up issues we've mentioned. |
celinval
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add to your TODO list that command line arguments should take precedence over flags on Cargo.toml?
Thanks
celinval
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch
| } else { | ||
| ctx.run_cbmc(&linked_obj)?; | ||
| let result = ctx.run_cbmc(&linked_obj)?; | ||
| if result == VerificationStatus::Failure { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: next time you can use if let
Description of changes:
This is a port of our python/bash scripts to native Rust implementations.
kaniandcargo-kani.arg.rsKaniSessionfromsession.rsto serve as the ambient background data/state for an execution.call_*.rsfiles.Major TODOs:
cargo-kani --visualizeCurrently known to be missing:
--gen-c-runnable(producing--symbol-table-passes)Resolved issues:
cargo-rmctests fail if we do not add$(pwd)to--build-base#755. I'm not sure how to interpret that issue, but I think we switch to changing the directory thatcargogets called in, which maybe resolves that issue?Call-outs:
Testing:
How is this change tested? existing regressions
Is this a refactor change? no
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.