-
Notifications
You must be signed in to change notification settings - Fork 110
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
Using function contracts to verify insertion into HashMap runs into OOM (>8GB) #3965
Comments
Hi! We're glad you're using Kani. I just started looking into this--I'll circle back with some suggestions once I've had a chance to dig in further. |
Thank you @carolynzech for looking into this!🙏 In the meantime I've set up a (for now private) GitHub Codespace where I have 16GB RAM available. When running the function contract in the OP with the GitHub Codespace, it failed with a CBMC error code 15 when system reached ~15GB (not quite sure how much RAM exactly was used until the error happened, cause I used I couldn't find anything on the internet regarding CBMC erroring with code 15. Note: I haven't gotten this error, when running on my local machine (with 8GB). Maybe it is because my local machine has a swap partition and GitHub Codespace has no swap? However, regarding no swap of GitHub Codespace, this is just guessing from my side. Please let me know, if I can assist any further or should provide you with more information. Really appreciate your effort!❤ |
Thanks for the info. CBMC provides its exit codes in this file: https://github.com/diffblue/cbmc/blob/fab67109ec0c3132b41a95bfe9c503f71fe9b329/src/util/exit_codes.h#L10 and out of memory is exit code 6, so that is indeed confusing. It's likely an error code returned by the system that killed CBMC, rather than an error code from CBMC itself. I tried to improve performance on your example, but it is generally very hard to get HashMaps and contracts to play nicely together. There's work we can (and should) do to make memory/runtime improvements there, but those are unfortunately nontrivial and not something we'll have done anytime super soon. I have two suggestions for things you can try:
#[kani::requires(x < 10)]
#[kani::ensures(|result| *result == 100)]
fn foo(x: u8) -> u8 { ... }
#[kani::proof_for_contract(foo)]
fn foo_harness() {
let x: u8 = kani::any();
foo(x);
} is equivalent to: fn foo(x: u8) -> u8 { ... }
#[kani::proof]
fn foo_harness() {
let x: u8 = kani::any();
kani::assume(x < 10);
let result = foo(x);
kani::assert(result == 100);
} If fn bar_harness() {
let x: u8 = kani::any();
let foo_result = foo(x);
kani::assume(foo_result == 100);
bar(foo_result);
}
Generally speaking, symbolic execution with HashMaps is a difficult problem, so while we definitely want to improve Kani's performance here, those improvements may not come quickly enough for you to make use of them very soon. Please let us know if there's anything else we can help with, or if there's a breakdown of the proof approach that you'd like us to look at. |
@janriemer You could also try using Bolero (https://github.com/camshaft/bolero). |
Wow, thank you @carolynzech for looking into it so deeply!❤
Hm...interesting. Thank you for pointing me to the error codes. Maybe it is because of the docker container in my Codespace not having all the necessary dependencies CBMC requires!? Anyway, this seems to be a separate issue then and we don't need to look into it for now.
Ohhh, I didn't know you could do that! This is brilliant! I'll definitely try this out. Thank you!😊
Yeah, this is an interesting idea.👍 Unfortunately, a primary goal of the algorithm is performance and I'd like to prevent regressions in that regard.
Ah, yes, I've already read about it in the kani docs and on Kani's blog. If my approach with Kani should fail, I'll definitely look into fuzzing and/or property based testing.
You are so kind! Thank you so much for your elaborate answer and for providing such great support!❤ Your answer has really helped me to get "unblocked", because:
For me Kani is a technology that is as close to magic as one can possibly imagine!✨ Thank you for maintaining such an awesome tool! |
Fair point, although you can separate the verification of the whole algorithm from the verification of the part that actually performs the lookup. Put differently, going back to my suggestion to break up the verification into multiple methods, you can imagine a division of labor like this: fn lookup_value(map: HashMap<...>) { ... } -> ...
fn do_something_with_value(val: ...) { ... } where You may also want to look at Verus, another tool for verifying the correctness of Rust code. |
Ah, I see! This is a good idea - thank you!😊 I've just looked into how this might apply to the diffing algorithm I want to verify and it turns out, an estimated ~75-80% of it can be verified this way.
Thank you for the recommendation.👍 Wasn't Verus the other verification tool (besides Kani) that is used in the effort of verifying the Rust std lib? I've also briefly looked into Creusot, which uses deductive verification, but I haven't really tried it yet. The formal verification rabbit hole is so deep!🐰🕳 I love it, but also...help!?😂 |
Hi all,
Goal that I try to achieve
I'm trying to verify the diffing algorithm in
csv-diff
with kani.In the algorithm itself, a lot of operations on hash maps are happening, mainly insertions and/or updates.
Problem I run into
When trying to impl a kani proof for it (first without function contracts), memory usage is fine (~6GB), but after letting it run for over 1,5h, I killed it, because the algorithm seems to be too complicated to verify it fully automatically.
So I resort to kani's function contracts in the hope of composing smaller proofs together (via
proof_for_contract
andstub_verified
).In order to "test the waters", I've tried to proof a very simple "dummy" function that adds/updates a single hash map entry with a function contract, but unfortunately when executing the
proof_for_contract
, even this tiny example blew up my memory:=> I only have 8GB RAM available, but those 8GB were filled very quickly during the execution.
So my question is: is it normal that this much RAM is required for function contracts? Trying to verify the same "dummy" function with normal proof harness finishes very fast and doesn't require lots of RAM.
I guess it might have something to do with the
modifies
clause not being properly constraint (in case ofproof_for_contract
), but also I don't know how to constrain it any further (see code below).Any help is greatly appreciated!❤️
Code to reproduce
I tried this code (function contract on the "dummy" function that does some hash map operation):
using the following command line invocation:
with Kani version: 0.58
The text was updated successfully, but these errors were encountered: