-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
Miri casts: do not blindly rely on dest type #72525
Conversation
r? @cramertj (rust_highfive has picked a reviewer for you, use r? to override) |
Dang, looks like this short-cut I took for unsizing coercions doesn't work in
|
This comment has been minimized.
This comment has been minimized.
All right, fixed that. |
self.write_immediate(res, dest)?; | ||
} | ||
|
||
Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => { | ||
// These are NOPs, but can be wide pointers. |
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.
Not important but I wonder if "noop" or "no-op" is more common.
dest: PlaceTy<'tcx, M::PointerTag>, | ||
) -> InterpResult<'tcx> { | ||
use rustc_middle::mir::CastKind::*; | ||
match kind { | ||
// FIXME: In which cases should we trigger UB when the source is uninit? |
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.
Hmm presumably the non-UB cases are at most those that slice/reuse bits? (so some pointer casts and integer truncations/zero-extensions)
Sign-extensions and any int <-> float casts should definitely be UB on uninit (and I'm guessing they are already?)
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.
Sign-extensions and any int <-> float casts should definitely be UB on uninit (and I'm guessing they are already?)
Yeah anything that looks at the bits is already UB. But e.g. currently casting one raw ptr type to another would not be UB on uninitialized inputs. Maybe that's good, maybe not.
let v = self.read_immediate(src)?; | ||
self.write_immediate(*v, dest)?; |
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 this relying on the type/layout equality check being forgiving around raw pointers?
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.
Hm... actually I am not sure I am happy with this. write_immediate
cannot know the layout of the immediate so in particular when it is a ScalarPair
it cannot know the offset of the 2nd field. We do check that both fields have the same size as what dest
expects, but I am not sure if that is enough?
self.write_immediate(res, dest)?; | ||
} | ||
|
||
Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => { |
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.
ArrayToPointer
is confusing, I'm guessing ArrayToElemPointer
makes more sense (or should it be ArayRefToElemPointer
? since a regular raw pointer cast shouldn't care about the pointee type changing).
Although PointerCast
variants might not need the Pointer
suffix. Anyway, this is not that important to this PR.
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 well I never understood most of these (and at some point whoever designed this clearly gave up and stuffed everything else into Misc
).
assert_eq!(src.layout.size, 2 * self.memory.pointer_size()); | ||
assert_eq!(dest_layout.size, self.memory.pointer_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.
Uhh, this is a bit hacky, but I guess pre-existing. Should probably rely on wide pointers having their first field be a simple pointer. So really just need to extract the first field and then cast it as usual.
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 that's basically what we do except we also have a bunch of extra assertions?
src/librustc_mir/interpret/cast.rs
Outdated
let size = match cast_ty.kind { | ||
// FIXME: Isn't there a helper for this? The same pattern occurs below. | ||
Int(t) => { | ||
t.bit_width().map(Size::from_bits).unwrap_or_else(|| self.pointer_size()) | ||
} | ||
Uint(t) => { | ||
t.bit_width().map(Size::from_bits).unwrap_or_else(|| self.pointer_size()) | ||
} | ||
RawPtr(_) => self.pointer_size(), | ||
_ => bug!(), | ||
}; |
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.
So much work to avoid calling layout_of
😁
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 learned the hard way that layout_of
is expensive...
trace!("Unsizing {:?} of type {} into {:?}", *src, src.layout.ty, cast_ty.ty); | ||
match (&src.layout.ty.kind, &cast_ty.ty.kind) { | ||
(&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. })) | ||
| (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => { | ||
self.unsize_into_ptr(src, dest, s, c) |
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.
Hmm, in other parts of the compiler these are clearly marked "source" and "target", but I guess not consistently enough.
Either way, feel free to use "target" to be more symmetrical. (But we can leave it as-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.
"target" and "dest" are a bit too similar for my taste...
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.
r=me with or without comments addressed (since this is mostly a refactor, with the only functional change AFAICT being which type is used as the cast target type)
@bors r=eddyb |
📌 Commit 8b5ba4a has been approved by |
Miri casts: do not blindly rely on dest type Make sure that we notice when the MIR is bad and the casted-to and destination type are e.g. of different size, as suggested by @eddyb.
Miri casts: do not blindly rely on dest type Make sure that we notice when the MIR is bad and the casted-to and destination type are e.g. of different size, as suggested by @eddyb.
r? @eddyb (reflecting who already reviewed this) |
Rollup of 5 pull requests Successful merges: - rust-lang#71940 (Add `len` and `slice_from_raw_parts` to `NonNull<[T]>`) - rust-lang#72525 (Miri casts: do not blindly rely on dest type) - rust-lang#72537 (Fix InlineAsmOperand expresions being visited twice during liveness checking) - rust-lang#72544 (librustc_middle: Rename upvars query to upvars_mentioned) - rust-lang#72551 (First draft documenting Debug stability.) Failed merges: r? @ghost
Make sure that we notice when the MIR is bad and the casted-to and destination type are e.g. of different size, as suggested by @eddyb.