-
Notifications
You must be signed in to change notification settings - Fork 94
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
RFC for Rust UB checks #3092
base: main
Are you sure you want to change the base?
RFC for Rust UB checks #3092
Conversation
rfc/src/rfcs/0010-rust-ub-checks.md
Outdated
|
||
A counter example that triggers UB may require extra tooling for debugging, e.g.: MIRI or valgrind. | ||
We propose that tests added by concrete playback in those cases should include a comment stating such limitation. | ||
Other possibilities are discussed in [0010-rust-ub-checks.md#open-questions]. |
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 here for link.
rfc/src/rfcs/0010-rust-ub-checks.md
Outdated
The default behavior matches the one that we believe is the recommended for most users due to soundness. | ||
In some cases, users may need to disable some checks for reasons such as spurious CEX or proof scalability. | ||
Thus, we suggest that every category of checks should have an opt-out mechanism that can either be global or local; | ||
i.e.: via command argument or harness attribute. |
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.
Since some of our checks are over-approximating, we may also need to introduce code-level control at some point, e.g. to enable/disable the checks for a few lines of code.
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.
Indeed. It might be hard for dependencies though.
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.
+1
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 OK if I leave this as open question?
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.
Thanks, @celinval ! I'm all down for a more consistent and intuitive experience like the one proposed in this RFC.
However, I'd have liked to see some content regarding categorization of UB checks. At the same time, I acknowledge that this subject deserves its own RFC, so I'd be satisfied if this RFC included a forward declaration about a future categorization effort that tries to be as exhaustive as possible (think of it as a more detailed version of this reference section).
rfc/src/rfcs/0010-rust-ub-checks.md
Outdated
detected undefined behavior. | ||
|
||
For simplicity, we propose that all undefined behavior checks are modelled as assert-assume pairs. | ||
We propose that the status of all passing assertions that may be affected by this check to be displayed as |
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 it'll become clearer to me later, but this sentence seems quite ambiguous to me. Does "may be affected" here mean "goes after"?
Also have some concerns about this UX point, but let me read till the end and will come back later.
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, I don't think this became clearer to me after reviewing the rest of the RFC. Why only passing assertions? Don't you want to refer to any checks in general, also regardless of their status?
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 believe we have two sound options here:
- Change the status of ALL passing assertions as undetermined.
- Change the status of passing assertions only if they are downstream from the UB check that has failed. I.e., do something similar to what is being proposed in the Fatal Assertions PR (introduce 'fatal assertions' diffblue/cbmc#8226). In this case, you are traversing the control flow graph to rule out assertions that can only occur before the UB check.
I did leave this slightly ambiguous on purpose. Both options above are an over-approximation of the set of assertions that could be reached after the UB. The option 2 is an optimization of option 1, and I'm curious on how function pointers and other things will play out here.
In my opinion, they are both acceptable (as long as they are sound) and better than what we have today. I am not convinced the optimization adds a lot of value, since users shouldn't really be using partial results from a program with UB.
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 tried to make it more explicit. Let me know if that makes sense now.
rfc/src/rfcs/0010-rust-ub-checks.md
Outdated
fn buggy_code() { | ||
let mut value: bool = false; | ||
unsafe { | ||
let ptr: *mut u8 = &mut value as *mut _ as *mut u8; |
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 is the double as * mut <type>
needed here?
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.
Because we first need to cast the reference to a raw pointer, then we need to cast to a u8
pointer.
rfc/src/rfcs/0010-rust-ub-checks.md
Outdated
able to detect efficiently. For example, we could fail if casting between types that have different value | ||
requirements, i.e.: fail in the cast `*mut bool as *mut u8`. | ||
|
||
For option 2, Kani may fail verification even if the code does not have UB. |
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 don't like this option because I think it'll reduce Kani's usability in such cases. The unsafe code is likely to stay as it is, forcing users to introduce verification-specific code that may or may not be equivalent to the code not triggering UB in the first place.
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 agree that this is not ideal, but the ideal solution may not scale to basic examples.
In cases where option 1 is not feasible, or not yet implemented, option 2 may enable us to verify most existing code out there and fail for very specific use cases.
For example, we won't likely be able to implement a full stacked borrows in Kani since the runtime overhead can be overwhelming. In that case, modeling an over-abstraction may allow us to reason about aliasing violation in most use cases, but we may need to reject some code that is accepted by Miri.
I would be OK with that, as long as the failure explicitly mentions that this may be due to an over-approximation. Users would still be able to run the counter-example in Miri to check if it is spurious or not. Maybe we can even eventually integrate with Miri to check that.
rfc/src/rfcs/0010-rust-ub-checks.md
Outdated
The default behavior matches the one that we believe is the recommended for most users due to soundness. | ||
In some cases, users may need to disable some checks for reasons such as spurious CEX or proof scalability. | ||
Thus, we suggest that every category of checks should have an opt-out mechanism that can either be global or local; | ||
i.e.: via command argument or harness attribute. |
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.
+1
rfc/src/rfcs/0010-rust-ub-checks.md
Outdated
- **Feature Name:** Rust UB Checks (`rust-ub-checks`) | ||
- **Feature Request Issue:** [#3089](https://github.com/model-checking/kani/issues/3089) | ||
- **RFC PR:** [#XXXX](https://github.com/model-checking/kani/pull/3091) | ||
- **Status:** Under Review |
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 pick: I suppose the above need to be updated?!
In the previous PR model-checking#3085, we did not support checks for `write_bytes` which is added in this PR. I am waiting for model-checking#3092 to add expected tests.
Conflicts: - rfc/src/SUMMARY.md
- Changed the example. - Improved text. - Removed restriction on unstable opt-out.
4f6c75f
to
96f50da
Compare
Create a new RFC for standardizing Kani's UB checks.
I left the "Software Design" section empty for now so we can first focus on the user experience.
Note that this RFC might need some adjustment depending on the outcome of diffblue/cbmc#8242 and diffblue/cbmc#8226.
Related to #3089
Rendered version: https://celinval.github.io/kani-dev/rfc/rfcs/0010-rust-ub-checks.html
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.