Repository navigation
implement intptrcast model #224
Copy link
Copy link
Closed
Labels
A-interpreterArea: affects the core interpreterArea: affects the core interpreterA-intptrcastArea: affects int2ptr and ptr2int castsArea: affects int2ptr and ptr2int castsC-projectCategory: a larger project is being tracked here, usually with checkmarks for individual stepsCategory: a larger project is being tracked here, usually with checkmarks for individual steps
Description
Since right now miri's pointer comparison bails out on &x as *const T < &y as *const T but allows &x as *const T == &y as *const T, I suggest to also allow <. This will not impact ctfe-mode, since ctfe mode disallows any binary ops on pointers into different allocs, even ==.
It's deterministic between multiple miri runs, doesn't leak HashMap ordering and only depends on optimizations and codegen from rustc (but that's already true for ==).
Steps to do:
- Implement force_bits, force_int and basic address assignment.
- force_bits when doing primitive operations.
- force_bits when doing casts.
- force_ptr when creating a non-ZST reference. (Ideally through
check_ptr_access? Redundant with validity checks but whatever.) We may need aforce_ptr_placemethod or so; the freeze-sensitive visitor also needs that. - Make force_bits infallible (in Miri, not CTFE) -- see Make force_ptr a total operation, and ptr-to-int-casts not require extra machine state #841.
-
force_bits when doing partial loads of a pointer -- and really everywhere we need a ptr, once it is total. (For now let's not do this, also see const-eval: load of partially initialized scalar produces entirely uninitialized result rust#69488 for a similar problem around partially initialized data.) - Exploit "real" pointer alignment for alignment checks, but warn when we do. (But also see align_offset guarantees rust#62420.)
- Ptr-to-int of a dangling pointer (possible in safe code).
- Fix the FIXMEs added in Add tests for Intptrcast when doing explicit casts #803.
- Audit all remaining uses of to_bits (this includes the
to_<int type>methods...), to_ptr and to_bits_or_ptr, all uses of is_ptr and is_bits, and also all uses offorce_ptrandforce_mplace_ptr(alsomplace_access_checked,check_mplace_access) outside ofcheck_ptr_access. Maybe replace to_{bits,ptr} by assert_{bits,ptr}?
Metadata
Metadata
Assignees
Labels
A-interpreterArea: affects the core interpreterArea: affects the core interpreterA-intptrcastArea: affects int2ptr and ptr2int castsArea: affects int2ptr and ptr2int castsC-projectCategory: a larger project is being tracked here, usually with checkmarks for individual stepsCategory: a larger project is being tracked here, usually with checkmarks for individual steps
Activity
RalfJung commentedon Jun 28, 2017
My plan was to go even further and essentially implement the intptrcast-model. That would immediately make us support all pointer arithmetic.
Essentially, I think at some point miri should (in non-CTFE mode!) guarantee that safe Rust code can not error out (if the unsafe libraries it uses don't contain bugs). So hashing raw pointers and printing them and stuff like that all have to work.
oli-obk commentedon Jun 29, 2017
Sounds reasonable to me. Once a pointer has been observed and thus it's integral representation is available, there's no way in safe code to go from that pointer back to the allocation, so we don't even need to support that direction, right? All we need to ensure is hat the integral representation doesn't change when obtaining it multiple times from a real pointer
We'll also need to generate an integer address when offsetting pointers due to overflow concerns. Overflowing exposes the address essentially.
RalfJung commentedon Jun 29, 2017
That's only true when you do the offsetting after casting to integer though, is it? The offset operations on pointers don't let safe code observe whether an overflow actually happened. Really, we could just generate an integer address immediately on allocation.
Doing lookup on a
Pointeris still more efficient, so that may be a reason to convert back.oli-obk commentedon Jun 30, 2017
Well... You can do
wrapping_offset(safe and stable method), and then do a<=comparison between the resulting pointer and the new one.We'd run out of integer addresses eventually (and quickly on 32bit emulation) if we don't reuse integer addresses and create a unique integer address for every allocation.
RalfJung commentedon Jul 3, 2017
Heh, nice catch.
"Quickly"? 2^32 is still a large number, I feel.^^
But anyway, I guess there's no problem with lazy allocation. That also gives us a nice single point of control for rejecting everything that could potentially leak the base address when in CTFE mode -- we just always make the allocation of an integer address fail in that mode.
oli-obk commentedon Jul 3, 2017
The problem is, that once you leak the base address of an array, you also leak the address of the last element's last byte. So allocatingand deallocating 100MB 50 times would fill the address space unless we allow reusing it.
RalfJung commentedon Aug 4, 2017
Actually, given what I wrote in https://internals.rust-lang.org/t/types-as-contracts/5562/81, I think it would make sense for miri to both support pointer comparisons on "abstract" pointers and only provide intptrcast optionally. Without intptrcast, there will always be safe code that miri cannot run (like printing the value of a pointer), but currently there still seems to be value in also having a mode that keeps pointers and integers more separate.
[-]Allow pointer comparison even between pointers of different allocs[/-][+]Allow pointer inequality even between pointers of different allocs[/+][-]Allow pointer inequality even between pointers of different allocs[/-][+]implement intptrcast model[/+]Amanieu commentedon Feb 6, 2019
This code currently fails on miri:
RalfJung commentedon Mar 8, 2019
Blocked on #653
oli-obk commentedon May 27, 2019
cc @christianpoveda
Basic (meaning probably incomplete) implementation instructions:
fn int_to_ptr(&self, ptr: Scalar) -> InterpResult<'tcx, Pointer>method toMachine(in rustc). This function should have a default impl that matches onScalar, returns thePointerif it's thePtrvariant and returns theReadBytesAsPointererror variant otherwiseReadBytesAsPointeris being emitted and callM::int_to_ptr(self, scalar)at said sites instead of emitting the error.ReadPointerAsBytes(but name the newly added methodptr_to_int(&self, ptr: Pointer) -> InterpResult<'tcx, Scalar>and fully ignore the argument and just emit the error variant)FxHashMap<AllocId, u64>tomiri/src/lib.rs
Line 324 in 3c930e4
Machine::ptr_to_intinside miriu64toEvaluator, which is the current number of bytes used up by the intptr "memory".FxHashMapduringptr_to_int, doname_of_the_hash_map.insert(ptr.alloc_id, name_of_the_u64);, computename_of_the_u64 + ptr.offsetin a variable, addmemory.get(id).bytes.len()to theu64, and return a newScalar created from theu64 + offset` variable. This will allow us to go the one way and do it deterministicallyEvaluator, this time to go from an integer to anAllocId(so the inverse of the other map). But since we may have added integers to the base address, you can't just compute the inverse map as that would not have entries for every single pointer. Instead we need something like aBTreeMap<u64, AllocId>(which also needs to be filled in inptr_to_int). UnfortunatelyBTreeMapdoesn't quite support our use case. What we want to find is the entry which our integer points into. So I guess we go with aVec<(u64, AllocId)>and do a binary search on it, which should be just as fast for finding elements as searching in aBTreeMap.if self.seed.is_none() { return err!(...); }to both of the methods to make sure we only do this behavior in nondeterministic mode.pvdrz commentedon May 27, 2019
Ok I'll start working on this :D
i'll be working on this branch: https://github.com/christianpoveda/rust/tree/intptrcast-model
20 remaining items
Auto merge of #779 - christianpoveda:intptrcast-model, r=RalfJung,oli…
oli-obk commentedon Jun 28, 2019
Some discussion between eddyb and me about miri and threads came to the conclusion that for certain kind of checks it would be helpful to only have the intptrcast part where you can go from ptr to int, but not the other way. I believe that would even cover most real use cases, as I've only seen the other direction in artificial examples so far
RalfJung commentedon Jun 28, 2019
I don't understand what you even mean by that. Could you give some concrete examples of programs we should not support and why?
Also see this Zulip discussion.
oli-obk commentedon Jun 28, 2019
well, basically I want to forbid (on 64 bit)
or any other bit modifying operations beyond those below the alignment (so those that will happen on the integer representation instead of on the abstract representation).
You can do these reading operations, so you can print all bits of a pointer, I just want to forbid
int2ptreverywhere.RalfJung commentedon Jun 28, 2019
I'd rather get rid of all the code that performs arithmetic on the
Pointerrepresentation. That code is a mess.Why don't you want to allow that code? I do want to forbid this code which is why I added a test for this, but yours seems fine to me.
oli-obk commentedon Jun 28, 2019
Essentially to be able to do full thread separation via our symbolic pointers. I want to be able to leverage the knowledge about what is accessible where. If we have integer pointers, that becomes impossible.
RalfJung commentedon Jun 28, 2019
I'm afraid you will have to tell me more, I still don't know what this is all about.^^
Can you open a new issue for this specific restriction, and describe in more detail the why and how? My plan is to close this one once everything works.
I should probably put a checklist in the first comment here for what is still on @christianpoveda's TODO list. ;)
pvdrz commentedon Jun 28, 2019
Plz do
RalfJung commentedon Jun 28, 2019
Done. :)
RalfJung commentedon Jul 28, 2019
Turns out instead of calling
force_ptrinRef, we can have the invariant that places are actually "normalized" toPointeronDeref, which happens in rust-lang/rust#63075. So that checkbox is also taken care of.Auto merge of #1851 - RalfJung:provenance-overhaul, r=RalfJung