-
Notifications
You must be signed in to change notification settings - Fork 12
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
Initial formal verification results #69
Conversation
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Wojciech Ozga <woz@zurich.ibm.com>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
I still want to make a pass over the annotations and add a bit more documentation before merging it. |
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 left a few comments proposing minor changes. I think you are trying to bring the old version of page_allocator which can be simply deleted.
@@ -117,6 +181,7 @@ impl MemoryLayout { | |||
/// | |||
/// Caller must guarantee that there is no other thread that can write to confidential memory during execution of | |||
/// this function. | |||
// TODO: we need to come up with a mechanism to acquire ownership of all memory |
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 function is called only when the security monitor is terminating, e.g., due to panic. So the procedure is to first stop all other harts and then execute this function. Since there are no other harts running, we have the ownership of whole confidential memory. I do not know how to express it for the spec purposes, 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.
Yeah, expressing this protocol in specification will be difficult -- but it is also not something we need to currently do, since we currently anyways want to prove that there are no panics, so this handler should not be called.
let mut smaller_pages = self.page_token.take().unwrap().divide(); | ||
assert!(smaller_pages.len() == self.children.len()); | ||
smaller_pages.drain(..).for_each(|smaller_page_token| { | ||
let index = self.calculate_child_index(this_node_base_address, this_node_page_size, &smaller_page_token); | ||
self.children[index].store_page_token_in_this_node(smaller_page_token); | ||
}); | ||
let smaller_size = self.max_allocable_page_size.unwrap().smaller().unwrap(); | ||
self.max_allocable_page_size = Some(smaller_size); |
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.
well spotted!
I think it was still working fine because the max_allocable_page_size was then recalculated again based on children's largest available page_size. But, with your fix we satisfy the spec.
pub fn acquire_page_token( | ||
&mut self, this_node_base_address: usize, this_node_page_size: PageSize, page_size_to_acquire: PageSize, | ||
) -> Result<Page<UnAllocated>, Error> { | ||
assert!(self.max_allocable_page_size >= Some(page_size_to_acquire)); | ||
//assert!(self.max_allocable_page_size >= Some(page_size_to_acquire)); | ||
ensure!(self.max_allocable_page_size >= Some(page_size_to_acquire), Error::OutOfPages())?; |
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.
well spotted!
looks like it was actually a bug and when we would try to allocate a page that is not available, this code would panic. Something to keep for the paper to say that using formal methods helps to spot these kinds of bugs
#[rr::args(#raw "#(-[#l; #sz; #tt])")] | ||
#[rr::returns("l +ₗ page_size_in_bytes_Z sz")] | ||
pub fn end_address_ptr(&self) -> *const usize { | ||
// TODO: ideally, use strict-provenance API |
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 you mean?
#![feature(strict_provenance)]
#![warn(fuzzy_provenance_casts)]
We should indeed use it
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.
Yeah, exactly. We should ideally use pointer::addr()
to convert pointers to integers (instead of as usize
), and pointer::with_addr()
to convert integers to pointers within a known allocation. This will make reasoning quite a bit easier.
This is not something we need to fix now, but I could make a follow-up MR that adapts the code for that.
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.
Sounds good to me. We can add it with another MR.
Co-authored-by: Wojciech Ozga <woz@zurich.ibm.com> Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
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.
Thank you for all the changes. It looks good to me!
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
616fb6f
to
8ce19f4
Compare
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.
LGTM
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 not thrilled with current path names: verification/generated_code/ace/proofs/
, because it is under generated_code
does not look like a manually curated / checked in object. However, I don't have any better suggestions at the moment :-). Anyway, the README.md clearly explains things, and I do not think bikeshedding the path name should hold up this PR.
Thanks for reviewing! I agree that the path name is not ideal. We could change it to |
I agree that would be a better name. Also, do we need the ace/ subfolder? |
## Architecture | ||
We annotate the Rust source code of the security monitor with verification annotations. | ||
These include invariants as well as pre- and postconditions. | ||
|
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.
In general the person needs to 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.
What do you mean? Would you like one of The user annotates...
or The Rust sourcecode is annotated...
(passive voice) better? Or do you have something else in mind?
verification/readme.md
Outdated
This is initiated by running `make verify` in the root directory of the repository. | ||
|
||
The generated Coq code is placed in the [`verification/generated_code/ace`](/verification/generated_code/ace) folder. | ||
It is split into two subfolders, [`generated`](/verification/generated_code/ace/generated) and [`proofs`](/verification/generated_code/ace/proofs). |
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.
There are two subfolders,
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.
if this was intended as a reply to my comment above:
yes, generated and proofs. I was referring to ace. concretely, I am proposing
rust_proofs/generated
and rust_proofs/proofs
(or maybe even rust_proofs/generated
and rust_proofs/src
?)
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 proofs
is a bit clearer than src
verification/readme.md
Outdated
The [`generated`](/verification/generated_code/ace/generated) subfolder contains the automatically generated model of the Rust code, the translated specifications, and proof templates. | ||
It is re-generated on every run of `make verify` and thus should not be modified manually. | ||
The [`proofs`](/verification/generated_code/ace/proofs) subfolder contains the proofs for the individual functions. | ||
These files may be modified, as they are not touched by RefinedRust after their initial creation, and may contain partially manual proofs. |
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.
These files can be modified because they are not touched by RefinedRust after their initial creation. If the proof is not complete (correct) manual proof steps will need to be added to complete the proof of the function.
The phrase about manual proofs is correct, but manual proofs would not be generated by refined rust.
I am not sure about what you mean by "after their initial creation". For example, if someone goes in a modified the Rust code eventually should that cause everything to be regenerated? All of our annotations are manual right now, so if the rust code is modified does someone has to add/inspect/the annotations? This may be one of the areas where we would like to add some automation ...
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'll change the text to your suggestion, that's a good point.
If someone modifies the Rust code and this breaks the proofs, then someone will have to fix the proofs. This may be because the annotations are now wrong/ the specification is wrong.
The intended workflow is that you change the code, and if everything continues to work, you don't have to inspect anything. Only if something breaks, you know that some work is needed to get the proofs working again.
(Of course, if you change the Rust code by adding new functions, you will generally have to write specifications for those.)
Generally, there should not arise a situation where you need to regenerate everything (including the proof files).
- the page token implementation is documented extensively, so you may learn by example. | ||
|
||
## 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.
Question: do you mean some less interesting verified support modules are not in the table or do you mean we are not verifying "less interesting" support modules? Hopefully the former. If so, somewhere we need to list the less interesting modules that have been verified (does not have to be in the readme).
if we want this table to actually be the verification state then everything must be listed or pointed to so that someone can tell how far we are. ... This is an observation not necessarily required for NIST.
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 former, these are not in the table.
I think if someone wants to be able to tell precisely how far we are, they can check this by looking at the annotated Rust files. Anything very detailed is likely prone to getting outdated easily or imposing a high maintenance burden. Probably the high-level modules are something that people will be interested in, but not which auxiliary functions precisely are verified.
But maybe we can discuss this more offline, if it's not required for this MR?
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 we cannot keep the README to detailed because we will most likely get out of sync with the source code. So a high level concepts should be enough at this stage. Once the verification is complete we might think to be more specific because the frequency of changes will be much smaller. All in all, I would keep it like it is now. We can discuss during our Mondays sync meetings.
I guess the |
yes, I think that renaming to
|
Actually, it's currently called |
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
Signed-off-by: Lennard Gäher <l.gaeher@posteo.de>
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 made a pass over the changes after all comments and it looks ready to be merged. We can change the crate name in future PR. @lgaeher thank you for this contribution!
Description of the changes
This MR adds our initial verification results.
Type of change
How to test this PR?
Follow the instructions in
verification/setup.md
to set the formal verification up.Alternatively, the
verify
pipeline in CI tests the verification results.