-
Notifications
You must be signed in to change notification settings - Fork 50
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
Specs for raw pointers & ghost ownership for pointers #1169
Conversation
0af0d5c
to
e5062b9
Compare
This sounds like a spec on Is it true that |
no! that's precisely what we want to avoid |
Ah right, |
But then that would mean that program equality implies equality of provenance, which is wrong? |
Indeed... I'll push an updated version 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.
As for the DeepModel
impl for pointers, I don't know what to say: metadata should be taken into account, but we cannot say that the pointer is only the address and the metadata, because of provenance.
We could have an instance for Sized
types only?
@xldenis : is it possible to have two pointer of equal address and underlying types, but without the same metadata.
creusot-contracts/src/std/pointer.rs
Outdated
|
||
mod std { | ||
mod ptr { | ||
#[ensures(result.addr_logic() == 0)] |
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.
#[ensures(result.addr_logic() == 0)] | |
#[ensures(result == Self::null_logic())] |
Otherwise, null_logic
should be removed.
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 not sound wrt the existence of multiple nulls. We could remove Self::null_logic()
if it's too tempting to incorrectly use it to test nullity (instead of .is_null_logic()
). In principle, null_logic()
gives you the ability to talk about "a null pointer" in the logic, but I don't know how useful that is...
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 not sound is to assume that all the null types are equal. This is not what I am proposing here.
What I am proposing is essentially to assume that the null
function is deterministic (it always returns the same value). I am not completely sure, but this seems very reasonable.
Now, you may argue that this is useless, which I may agree on. But if we do not add this post-condition, then we should remove null_logic
, because I think this is useless too.
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, I agree. To sum things up, there are two choices:
- keep
Self::null_logic()
and specify that it is equal to the result of thenull()
program function (thus specifying thatnull()
is deterministic; - remove
Self::null_logic
and only useis_null()
/ compareaddr_logic()
to 0.
I'm leaning towards option 2, because having a logical NULL is not so useful and gives the wrong idea (that there is only one NULL pointer). Using is_null
instead of comparing to NULL is the better 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.
Makes sense, and this does not prevent us from adding this in the future.
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 pushed changes implementing Option 2, with an additional helper function is_null_logic()
which simply unfolds to .addr_logic() == 0
. (I found this convenient in practice because it mirrors is_null()
in program code.)
I think so: let s1: &[i32] = &[1];
let s2: &[i32] = &s1[..0];
let p1: *const [i32] = s1;
let p2: *const [i32] = s2; p1 and p2 are the same pointer, and have the same type, but their metadata is different (1 vs 0). |
Well, obviously... |
68dffad
to
23855d3
Compare
23855d3
to
1b80da0
Compare
Soundly updating the specifications in |
e7ce0c0
to
4ad15eb
Compare
Ah, and note (in the current version of the PR) how I've tried to handle |
You changes look good! |
4ad15eb
to
9024790
Compare
74b5cb1
to
ca90cd0
Compare
I've now added a |
ca90cd0
to
a1d7bdf
Compare
263ef40
to
5c14a0a
Compare
@dewert99 as a heads up, this PR (which evolved to include a new module for "ghost ownership tokens" for pointers) removes your I don't know if you were still using |
creusot-contracts/src/ptr_own.rs
Outdated
#[trusted] | ||
#[ensures(result.1.ptr() == result.0 && *result.1.val() == *val)] | ||
pub fn from_box<T: ?Sized>(val: Box<T>) -> (RawPtr<T>, GhostBox<PtrOwn<T>>) { | ||
assert!(core::mem::size_of_val::<T>(&*val) > 0, "PtrOwn doesn't support ZSTs"); |
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 are ZST unsupported? Is this because the address of two ZST objects are always the same or something like 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.
That's a good question, I don't know; I took the assert from ghost_ptr
(cc @dewert99 ?).
Could there be issues with using Box::{into,from}_raw
on ZSTs maybe?
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 problem is that if you call Box::new
on a ZST several times, you get the same pointer in the end. So the tokens are not unique.
struct ZST {}
fn p_of_mut<T>(p: &mut T) -> *mut T {
p
}
fn main() {
let mut b1 = Box::new(ZST{});
let mut b2 = Box::new(ZST{});
let b1p : *mut ZST = p_of_mut(&mut *b1);
let b2p : *mut ZST = p_of_mut(&mut *b2);
eprintln!("{b1p:?} {b2p:?}")
}
The example above prints "0x1 0x1".
We should add as a pre-condition the fact that the underlying value is not a ZST, but there is noway to do that currently in Creusot (I think).
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.
could it be achieved using a const
failure (instead of a dynamic one)?
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 T
is not Sized, then this is dynamic information only.
db87748
to
909b394
Compare
Thanks for the review! I'm not sure what to do regarding the ZST-related assert, but I'd rather merge this now and remove the assert later if it is needed and sound. |
6d81615
to
bad599a
Compare
The functionality provided by ghost_ptr can now be achieved by storing PtrOwn tokens in a FMap.
bad599a
to
bff588e
Compare
It is indeed needed (see above), and the current situation is "unsound" (Creusot is supposed to prevent panics). Butfixing this would require reasonning about the size of a value pointed to by a |
I'm extracting extern specs for basic raw pointer operations (mainly getting their address and comparing with null) from
ghost_ptr.rs
because I need them in another context. (cc @dewert99)In the process I went and skimmed the docs (https://doc.rust-lang.org/std/ptr/index.html and https://doc.rust-lang.org/std/primitive.pointer.html) and I ended up with a more conservative model of raw pointers than
ghost_ptr.rs
, where pointers always have some hidden "metadata" associated, and one basically can never logically deduce equality of pointers except by calling==
.I have two questions:
==
(there is noDeepModel
instance in particular). I would like to say that the DeepModel of a*mut T
is "itself" (and we know nothing more), and that ifp == q
returnstrue
then we know thatp = q
in the logic and alsop.addr_logic() = q.addr_logic()
. What do I need to write to express that in Creusot?If the current approach seems OK then I'll have a go at updating
ghost_ptr.rs
to use the new model.