-
Notifications
You must be signed in to change notification settings - Fork 352
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
ensure that integers cast to pointers will never point at a valid alloc, not even the zst alloc #81
Changes from 4 commits
921f5af
b2d476e
1c40fb0
d42a7d0
75f56eb
f71c31c
2c34d65
511fa40
4a39c22
14ff641
13f22f8
f77a0ab
e2091ff
1549c2d
4748587
5ee75c0
1c5c6cd
64155ff
fd68670
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -182,7 +182,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
ty: Ty<'tcx>, | ||
substs: &'tcx Substs<'tcx> | ||
) -> EvalResult<'tcx, Pointer> { | ||
let size = self.type_size_with_substs(ty, substs); | ||
let size = self.type_size_with_substs(ty, substs).expect("cannot alloc memory for unsized type"); | ||
let align = self.type_align_with_substs(ty, substs); | ||
self.memory.allocate(size, align) | ||
} | ||
|
@@ -203,10 +203,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
PrimVal::from_uint_with_size(n, self.memory.pointer_size()) | ||
} | ||
|
||
fn isize_primval(&self, n: i64) -> PrimVal { | ||
PrimVal::from_int_with_size(n, self.memory.pointer_size()) | ||
} | ||
|
||
fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { | ||
// FIXME: cache these allocs | ||
let ptr = self.memory.allocate(s.len(), 1)?; | ||
|
@@ -290,16 +286,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
self.tcx.normalize_associated_type(&substituted) | ||
} | ||
|
||
fn type_size(&self, ty: Ty<'tcx>) -> usize { | ||
fn type_size(&self, ty: Ty<'tcx>) -> Option<usize> { | ||
self.type_size_with_substs(ty, self.substs()) | ||
} | ||
|
||
fn type_align(&self, ty: Ty<'tcx>) -> usize { | ||
self.type_align_with_substs(ty, self.substs()) | ||
} | ||
|
||
fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { | ||
self.type_layout_with_substs(ty, substs).size(&self.tcx.data_layout).bytes() as usize | ||
fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> Option<usize> { | ||
let layout = self.type_layout_with_substs(ty, substs); | ||
if layout.is_unsized() { | ||
None | ||
} else { | ||
Some(layout.size(&self.tcx.data_layout).bytes() as usize) | ||
} | ||
} | ||
|
||
fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize { | ||
|
@@ -480,7 +481,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
|
||
Array { .. } => { | ||
let elem_size = match dest_ty.sty { | ||
ty::TyArray(elem_ty, _) => self.type_size(elem_ty) as u64, | ||
ty::TyArray(elem_ty, _) => self.type_size(elem_ty).expect("array elements are sized") as u64, | ||
_ => bug!("tried to assign {:?} to non-array type {:?}", kind, dest_ty), | ||
}; | ||
let offsets = (0..).map(|i| i * elem_size); | ||
|
@@ -518,7 +519,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
self.write_value(value, dest, value_ty)?; | ||
} else { | ||
assert_eq!(operands.len(), 0); | ||
let zero = self.isize_primval(0); | ||
let value_size = self.type_size(dest_ty).expect("pointer types are sized"); | ||
let zero = PrimVal::from_int_with_size(0, value_size); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why this change? I think we can assume There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't want an isize here. The rawnullablepointer opt also happens for nonpointers There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh, right, for NonZero things that aren't pointer-sized? I hate the |
||
self.write_primval(dest, zero)?; | ||
} | ||
} else { | ||
|
@@ -534,15 +536,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
} else { | ||
for operand in operands { | ||
let operand_ty = self.operand_ty(operand); | ||
assert_eq!(self.type_size(operand_ty), 0); | ||
assert_eq!(self.type_size(operand_ty), Some(0)); | ||
} | ||
let offset = self.nonnull_offset(dest_ty, nndiscr, discrfield)?; | ||
let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; | ||
|
||
// FIXME(solson) | ||
let dest = self.force_allocation(dest)?.to_ptr(); | ||
|
||
let dest = dest.offset(offset.bytes() as isize); | ||
try!(self.memory.write_isize(dest, 0)); | ||
let dest_size = self.type_size(ty).unwrap_or(self.memory.pointer_size()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In what cases can this default to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the unsized case. Since any unsized value is a pointer to the actual sized value and this optimization only cares about the pointer part, not the extra part There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What unsized case? EDIT: Nevermind, I now see the conversation lower down; let's continue there. |
||
try!(self.memory.write_int(dest, 0, dest_size)); | ||
} | ||
} else { | ||
bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); | ||
|
@@ -576,7 +579,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
ty::TyArray(elem_ty, n) => (elem_ty, n), | ||
_ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty), | ||
}; | ||
let elem_size = self.type_size(elem_ty); | ||
let elem_size = self.type_size(elem_ty).expect("repeat element type must be sized"); | ||
let value = self.eval_operand(operand)?; | ||
|
||
// FIXME(solson) | ||
|
@@ -689,7 +692,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
} | ||
} | ||
|
||
fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, Size> { | ||
fn nonnull_offset_and_ty(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { | ||
// Skip the constant 0 at the start meant for LLVM GEP. | ||
let mut path = discrfield.iter().skip(1).map(|&i| i as usize); | ||
|
||
|
@@ -704,10 +707,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
_ => bug!("non-enum for StructWrappedNullablePointer: {}", ty), | ||
}; | ||
|
||
self.field_path_offset(inner_ty, path) | ||
self.field_path_offset_and_ty(inner_ty, path) | ||
} | ||
|
||
fn field_path_offset<I: Iterator<Item = usize>>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<'tcx, Size> { | ||
fn field_path_offset_and_ty<I: Iterator<Item = usize>>(&self, mut ty: Ty<'tcx>, path: I) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { | ||
let mut offset = Size::from_bytes(0); | ||
|
||
// Skip the initial 0 intended for LLVM GEP. | ||
|
@@ -717,7 +720,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap(); | ||
} | ||
|
||
Ok(offset) | ||
Ok((offset, ty)) | ||
} | ||
|
||
fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { | ||
|
@@ -991,7 +994,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
let (base_ptr, _) = base.to_ptr_and_extra(); | ||
|
||
let (elem_ty, len) = base.elem_ty_and_len(base_ty); | ||
let elem_size = self.type_size(elem_ty); | ||
let elem_size = self.type_size(elem_ty).expect("slice element must be sized"); | ||
let n_ptr = self.eval_operand(operand)?; | ||
let usize = self.tcx.types.usize; | ||
let n = self.value_to_primval(n_ptr, usize)? | ||
|
@@ -1007,7 +1010,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
let (base_ptr, _) = base.to_ptr_and_extra(); | ||
|
||
let (elem_ty, n) = base.elem_ty_and_len(base_ty); | ||
let elem_size = self.type_size(elem_ty); | ||
let elem_size = self.type_size(elem_ty).expect("sequence element must be sized"); | ||
assert!(n >= min_length as u64); | ||
|
||
let index = if from_end { | ||
|
@@ -1026,7 +1029,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
let (base_ptr, _) = base.to_ptr_and_extra(); | ||
|
||
let (elem_ty, n) = base.elem_ty_and_len(base_ty); | ||
let elem_size = self.type_size(elem_ty); | ||
let elem_size = self.type_size(elem_ty).expect("slice element must be sized"); | ||
assert!((from as u64) <= n - (to as u64)); | ||
let ptr = base_ptr.offset(from as isize * elem_size as isize); | ||
let extra = LvalueExtra::Length(n - to as u64 - from as u64); | ||
|
@@ -1046,7 +1049,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
} | ||
|
||
fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { | ||
let size = self.type_size(ty); | ||
let size = self.type_size(ty).expect("cannot copy from an unsized type"); | ||
let align = self.type_align(ty); | ||
self.memory.copy(src, dest, size, align)?; | ||
Ok(()) | ||
|
@@ -1512,7 +1515,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
for (i, (src_f, dst_f)) in iter { | ||
let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a); | ||
let dst_fty = monomorphize_field_ty(self.tcx, dst_f, substs_b); | ||
if self.type_size(dst_fty) == 0 { | ||
if self.type_size(dst_fty) == Some(0) { | ||
continue; | ||
} | ||
let src_field_offset = self.get_field_offset(src_ty, i)?.bytes() as isize; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,7 +193,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
|
||
Abi::C => { | ||
let ty = fn_ty.sig.0.output; | ||
let size = self.type_size(ty); | ||
let size = self.type_size(ty).expect("function return type cannot be unsized"); | ||
let (ret, target) = destination.unwrap(); | ||
self.call_c_abi(def_id, arg_operands, ret, size)?; | ||
self.goto_block(target); | ||
|
@@ -263,14 +263,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
self.memory.read_int(adt_ptr, discr_size as usize)? as u64 | ||
} | ||
|
||
RawNullablePointer { nndiscr, .. } => { | ||
self.read_nonnull_discriminant_value(adt_ptr, nndiscr)? | ||
RawNullablePointer { nndiscr, value } => { | ||
let discr_size = value.size(&self.tcx.data_layout).bytes() as usize; | ||
self.read_nonnull_discriminant_value(adt_ptr, nndiscr, discr_size)? | ||
} | ||
|
||
StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { | ||
let offset = self.nonnull_offset(adt_ty, nndiscr, discrfield)?; | ||
let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; | ||
let nonnull = adt_ptr.offset(offset.bytes() as isize); | ||
self.read_nonnull_discriminant_value(nonnull, nndiscr)? | ||
// only the pointer part of a fat pointer is used for this space optimization | ||
let discr_size = self.type_size(ty).unwrap_or(self.memory.pointer_size()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto here. When can the nonnull field be a unsized? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can't be, but the MIR ends up giving us an index path that will give us the first field of the fat pointer, which ends up being te unsized value. Not sure if the code giving us the type path is wrong or if this is intended. I'll check and get back to you There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first field of the fat pointer is the thin data pointer. This is a GEP field path, it will not deref nested pointers, just offset into a flat structure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see. It sounds like we'd need to adjust the type, then, to match @eddyb's point. Alternately, maybe it can return the size instead of the type? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
self.read_nonnull_discriminant_value(nonnull, nndiscr, discr_size)? | ||
} | ||
|
||
// The discriminant_value intrinsic returns 0 for non-sum types. | ||
|
@@ -281,8 +284,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
Ok(discr_val) | ||
} | ||
|
||
fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64) -> EvalResult<'tcx, u64> { | ||
let not_null = match self.memory.read_usize(ptr) { | ||
fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u64, discr_size: usize) -> EvalResult<'tcx, u64> { | ||
let not_null = match self.memory.read_uint(ptr, discr_size) { | ||
Ok(0) => false, | ||
Ok(_) | Err(EvalError::ReadPointerAsBytes) => true, | ||
Err(e) => return Err(e), | ||
|
@@ -639,7 +642,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
}; | ||
let drop_fn = self.memory.read_ptr(vtable)?; | ||
// some values don't need to call a drop impl, so the value is null | ||
if !drop_fn.points_to_zst() { | ||
if drop_fn != Pointer::from_int(0) { | ||
let (def_id, substs, ty) = self.memory.get_fn(drop_fn.alloc_id)?; | ||
let fn_sig = self.tcx.erase_late_bound_regions_and_normalize(&ty.sig); | ||
let real_ty = fn_sig.inputs[0]; | ||
|
@@ -655,7 +658,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => (ptr, len as isize), | ||
_ => bug!("expected an lvalue with a length"), | ||
}; | ||
let size = self.type_size(elem_ty) as isize; | ||
let size = self.type_size(elem_ty).expect("slice element must be sized") as isize; | ||
// FIXME: this creates a lot of stack frames if the element type has | ||
// a drop impl | ||
for i in 0..len { | ||
|
@@ -668,7 +671,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { | |
Lvalue::Ptr { ptr, extra } => (ptr, extra), | ||
_ => bug!("expected an lvalue with optional extra data"), | ||
}; | ||
let size = self.type_size(elem_ty) as isize; | ||
let size = self.type_size(elem_ty).expect("array element cannot be unsized") as isize; | ||
// FIXME: this creates a lot of stack frames if the element type has | ||
// a drop impl | ||
for i in 0..len { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -73,7 +73,7 @@ impl Pointer { | |
// FIXME(solson): Integer pointers should use u64, not usize. Target pointers can be larger | ||
// than host usize. | ||
pub fn from_int(i: usize) -> Self { | ||
Pointer::new(ZST_ALLOC_ID, i) | ||
Pointer::new(NEVER_ALLOC_ID, i) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so I did this change to fix the assume bug, which then caused drop impls to fail: https://github.com/solson/miri/pull/81/files#diff-8f4a840e817b018e1d74153888641b27L642 and int to pointer cast error messages to show up as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems fine. I might have a plan to refactor |
||
} | ||
|
||
pub fn zst_ptr() -> Self { | ||
|
@@ -290,7 +290,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { | |
Some(alloc) => Ok(alloc), | ||
None => match self.functions.get(&id) { | ||
Some(_) => Err(EvalError::DerefFunctionPointer), | ||
None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), | ||
None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), | ||
None => Err(EvalError::DanglingPointerDeref), | ||
} | ||
} | ||
|
@@ -302,7 +302,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { | |
Some(alloc) => Ok(alloc), | ||
None => match self.functions.get(&id) { | ||
Some(_) => Err(EvalError::DerefFunctionPointer), | ||
None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), | ||
None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess), | ||
None => Err(EvalError::DanglingPointerDeref), | ||
} | ||
} | ||
|
@@ -570,7 +570,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { | |
2 => Ok(self.layout.i16_align.abi() as usize), | ||
4 => Ok(self.layout.i32_align.abi() as usize), | ||
8 => Ok(self.layout.i64_align.abi() as usize), | ||
_ => bug!("bad integer size"), | ||
_ => bug!("bad integer size: {}", size), | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fn main() { | ||
vec![()].into_iter(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#![allow(dead_code)] | ||
|
||
enum E { | ||
A = 1, | ||
B = 2, | ||
C = 3, | ||
} | ||
|
||
fn main() { | ||
let enone = None::<E>; | ||
if let Some(..) = enone { | ||
panic!(); | ||
} | ||
} |
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 doesn't this apply to general pointers casted to function pointers any more? The name
InvalidFunctionPointer
seems too general for the new error message.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.
@oli-obk pinging to make sure you don't miss this.
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.
Nothing changed here, the error message was outdated way before this pr, we can adjust the variant name to match 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.
What kind of error do we actually get when treating a regular pointer as a function pointer?
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.
We can't do
transmute(42)
because primvals panic in some cases right now. I think we should eliminate theexpect
functions and have thetry
functions return anEvalResult
instead.