align allocations in the worst possible way
This commit is contained in:
parent
4781a6ba54
commit
082effb3ee
8 changed files with 71 additions and 39 deletions
|
@ -36,6 +36,10 @@ pub enum EvalError<'tcx> {
|
||||||
},
|
},
|
||||||
ExecutionTimeLimitReached,
|
ExecutionTimeLimitReached,
|
||||||
StackFrameLimitReached,
|
StackFrameLimitReached,
|
||||||
|
AlignmentCheckFailed {
|
||||||
|
required: usize,
|
||||||
|
has: usize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
|
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
|
||||||
|
@ -82,6 +86,8 @@ impl<'tcx> Error for EvalError<'tcx> {
|
||||||
"reached the configured maximum execution time",
|
"reached the configured maximum execution time",
|
||||||
EvalError::StackFrameLimitReached =>
|
EvalError::StackFrameLimitReached =>
|
||||||
"reached the configured maximum number of stack frames",
|
"reached the configured maximum number of stack frames",
|
||||||
|
EvalError::AlignmentCheckFailed{..} =>
|
||||||
|
"tried to execute a misaligned read or write",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +112,9 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
|
||||||
EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } =>
|
EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } =>
|
||||||
write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory",
|
write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory",
|
||||||
allocation_size, memory_size - memory_usage, memory_size),
|
allocation_size, memory_size - memory_usage, memory_size),
|
||||||
|
EvalError::AlignmentCheckFailed { required, has } =>
|
||||||
|
write!(f, "tried to access memory with alignment {}, but alignment {} is required",
|
||||||
|
has, required),
|
||||||
_ => write!(f, "{}", self.description()),
|
_ => write!(f, "{}", self.description()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
match output_ty {
|
match output_ty {
|
||||||
ty::FnConverging(ty) => {
|
ty::FnConverging(ty) => {
|
||||||
let size = self.type_size_with_substs(ty, substs);
|
let size = self.type_size_with_substs(ty, substs);
|
||||||
self.memory.allocate(size).map(Some)
|
let align = self.type_align_with_substs(ty, substs);
|
||||||
|
self.memory.allocate(size, align).map(Some)
|
||||||
}
|
}
|
||||||
ty::FnDiverging => Ok(None),
|
ty::FnDiverging => Ok(None),
|
||||||
}
|
}
|
||||||
|
@ -177,7 +178,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
|
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
|
||||||
macro_rules! i2p {
|
macro_rules! i2p {
|
||||||
($i:ident, $n:expr) => {{
|
($i:ident, $n:expr) => {{
|
||||||
let ptr = self.memory.allocate($n)?;
|
let ptr = self.memory.allocate($n, $n)?;
|
||||||
self.memory.write_int(ptr, $i as i64, $n)?;
|
self.memory.write_int(ptr, $i as i64, $n)?;
|
||||||
Ok(ptr)
|
Ok(ptr)
|
||||||
}}
|
}}
|
||||||
|
@ -202,8 +203,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
Integral(ConstInt::Usize(ConstUsize::Us64(i))) => i2p!(i, 8),
|
Integral(ConstInt::Usize(ConstUsize::Us64(i))) => i2p!(i, 8),
|
||||||
Str(ref s) => {
|
Str(ref s) => {
|
||||||
let psize = self.memory.pointer_size();
|
let psize = self.memory.pointer_size();
|
||||||
let static_ptr = self.memory.allocate(s.len())?;
|
let static_ptr = self.memory.allocate(s.len(), 1)?;
|
||||||
let ptr = self.memory.allocate(psize * 2)?;
|
let ptr = self.memory.allocate(psize * 2, psize)?;
|
||||||
self.memory.write_bytes(static_ptr, s.as_bytes())?;
|
self.memory.write_bytes(static_ptr, s.as_bytes())?;
|
||||||
self.memory.write_ptr(ptr, static_ptr)?;
|
self.memory.write_ptr(ptr, static_ptr)?;
|
||||||
self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?;
|
self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?;
|
||||||
|
@ -211,19 +212,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
ByteStr(ref bs) => {
|
ByteStr(ref bs) => {
|
||||||
let psize = self.memory.pointer_size();
|
let psize = self.memory.pointer_size();
|
||||||
let static_ptr = self.memory.allocate(bs.len())?;
|
let static_ptr = self.memory.allocate(bs.len(), 1)?;
|
||||||
let ptr = self.memory.allocate(psize)?;
|
let ptr = self.memory.allocate(psize, psize)?;
|
||||||
self.memory.write_bytes(static_ptr, bs)?;
|
self.memory.write_bytes(static_ptr, bs)?;
|
||||||
self.memory.write_ptr(ptr, static_ptr)?;
|
self.memory.write_ptr(ptr, static_ptr)?;
|
||||||
Ok(ptr)
|
Ok(ptr)
|
||||||
}
|
}
|
||||||
Bool(b) => {
|
Bool(b) => {
|
||||||
let ptr = self.memory.allocate(1)?;
|
let ptr = self.memory.allocate(1, 1)?;
|
||||||
self.memory.write_bool(ptr, b)?;
|
self.memory.write_bool(ptr, b)?;
|
||||||
Ok(ptr)
|
Ok(ptr)
|
||||||
}
|
}
|
||||||
Char(c) => {
|
Char(c) => {
|
||||||
let ptr = self.memory.allocate(4)?;
|
let ptr = self.memory.allocate(4, 4)?;
|
||||||
self.memory.write_uint(ptr, c as u64, 4)?;
|
self.memory.write_uint(ptr, c as u64, 4)?;
|
||||||
Ok(ptr)
|
Ok(ptr)
|
||||||
},
|
},
|
||||||
|
@ -269,10 +270,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
self.type_size_with_substs(ty, self.substs())
|
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 {
|
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
|
self.type_layout_with_substs(ty, substs).size(&self.tcx.data_layout).bytes() as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize {
|
||||||
|
self.type_layout_with_substs(ty, substs).align(&self.tcx.data_layout).pref() as usize
|
||||||
|
}
|
||||||
|
|
||||||
fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout {
|
fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout {
|
||||||
self.type_layout_with_substs(ty, self.substs())
|
self.type_layout_with_substs(ty, self.substs())
|
||||||
}
|
}
|
||||||
|
@ -306,7 +315,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
|
|
||||||
let locals: EvalResult<'tcx, Vec<Pointer>> = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| {
|
let locals: EvalResult<'tcx, Vec<Pointer>> = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| {
|
||||||
let size = self.type_size_with_substs(ty, substs);
|
let size = self.type_size_with_substs(ty, substs);
|
||||||
self.memory.allocate(size)
|
let align = self.type_align_with_substs(ty, substs);
|
||||||
|
self.memory.allocate(size, align)
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
self.stack.push(Frame {
|
self.stack.push(Frame {
|
||||||
|
@ -553,7 +563,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
|
|
||||||
Box(ty) => {
|
Box(ty) => {
|
||||||
let size = self.type_size(ty);
|
let size = self.type_size(ty);
|
||||||
let ptr = self.memory.allocate(size)?;
|
let align = self.type_align(ty);
|
||||||
|
let ptr = self.memory.allocate(size, align)?;
|
||||||
self.memory.write_ptr(dest, ptr)?;
|
self.memory.write_ptr(dest, ptr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,7 +712,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
Item { def_id, substs } => {
|
Item { def_id, substs } => {
|
||||||
if let ty::TyFnDef(..) = ty.sty {
|
if let ty::TyFnDef(..) = ty.sty {
|
||||||
// function items are zero sized
|
// function items are zero sized
|
||||||
Ok(self.memory.allocate(0)?)
|
Ok(self.memory.allocate(0, 0)?)
|
||||||
} else {
|
} else {
|
||||||
let cid = ConstantId {
|
let cid = ConstantId {
|
||||||
def_id: def_id,
|
def_id: def_id,
|
||||||
|
@ -955,9 +966,9 @@ pub fn eval_main<'a, 'tcx: 'a>(
|
||||||
if mir.arg_decls.len() == 2 {
|
if mir.arg_decls.len() == 2 {
|
||||||
// start function
|
// start function
|
||||||
let ptr_size = ecx.memory().pointer_size();
|
let ptr_size = ecx.memory().pointer_size();
|
||||||
let nargs = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for nargs");
|
let nargs = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for nargs");
|
||||||
ecx.memory_mut().write_usize(nargs, 0).unwrap();
|
ecx.memory_mut().write_usize(nargs, 0).unwrap();
|
||||||
let args = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for arg pointer");
|
let args = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for arg pointer");
|
||||||
ecx.memory_mut().write_usize(args, 0).unwrap();
|
ecx.memory_mut().write_usize(args, 0).unwrap();
|
||||||
ecx.frame_mut().locals[0] = nargs;
|
ecx.frame_mut().locals[0] = nargs;
|
||||||
ecx.frame_mut().locals[1] = args;
|
ecx.frame_mut().locals[1] = args;
|
||||||
|
|
|
@ -88,7 +88,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
match func_ty.sty {
|
match func_ty.sty {
|
||||||
ty::TyFnPtr(bare_fn_ty) => {
|
ty::TyFnPtr(bare_fn_ty) => {
|
||||||
let ptr = self.eval_operand(func)?;
|
let ptr = self.eval_operand(func)?;
|
||||||
assert_eq!(ptr.offset, 0);
|
|
||||||
let fn_ptr = self.memory.read_ptr(ptr)?;
|
let fn_ptr = self.memory.read_ptr(ptr)?;
|
||||||
let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?;
|
let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?;
|
||||||
if fn_ty != bare_fn_ty {
|
if fn_ty != bare_fn_ty {
|
||||||
|
@ -416,14 +415,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||||
match &link_name[..] {
|
match &link_name[..] {
|
||||||
"__rust_allocate" => {
|
"__rust_allocate" => {
|
||||||
let size = self.memory.read_usize(args[0])?;
|
let size = self.memory.read_usize(args[0])?;
|
||||||
let ptr = self.memory.allocate(size as usize)?;
|
let align = self.memory.read_usize(args[1])?;
|
||||||
|
let ptr = self.memory.allocate(size as usize, align as usize)?;
|
||||||
self.memory.write_ptr(dest, ptr)?;
|
self.memory.write_ptr(dest, ptr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
"__rust_reallocate" => {
|
"__rust_reallocate" => {
|
||||||
let ptr = self.memory.read_ptr(args[0])?;
|
let ptr = self.memory.read_ptr(args[0])?;
|
||||||
let size = self.memory.read_usize(args[2])?;
|
let size = self.memory.read_usize(args[2])?;
|
||||||
let new_ptr = self.memory.reallocate(ptr, size as usize)?;
|
let align = self.memory.read_usize(args[3])?;
|
||||||
|
let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?;
|
||||||
self.memory.write_ptr(dest, new_ptr)?;
|
self.memory.write_ptr(dest, new_ptr)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub struct Allocation {
|
||||||
pub bytes: Vec<u8>,
|
pub bytes: Vec<u8>,
|
||||||
pub relocations: BTreeMap<usize, AllocId>,
|
pub relocations: BTreeMap<usize, AllocId>,
|
||||||
pub undef_mask: UndefMask,
|
pub undef_mask: UndefMask,
|
||||||
|
pub align: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
@ -98,10 +99,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
||||||
bytes: Vec::new(),
|
bytes: Vec::new(),
|
||||||
relocations: BTreeMap::new(),
|
relocations: BTreeMap::new(),
|
||||||
undef_mask: UndefMask::new(0),
|
undef_mask: UndefMask::new(0),
|
||||||
|
align: 0,
|
||||||
};
|
};
|
||||||
mem.alloc_map.insert(ZST_ALLOC_ID, alloc);
|
mem.alloc_map.insert(ZST_ALLOC_ID, alloc);
|
||||||
// check that additional zst allocs work
|
// check that additional zst allocs work
|
||||||
debug_assert!(mem.allocate(0).unwrap().points_to_zst());
|
debug_assert!(mem.allocate(0, 0).unwrap().points_to_zst());
|
||||||
debug_assert!(mem.get(ZST_ALLOC_ID).is_ok());
|
debug_assert!(mem.get(ZST_ALLOC_ID).is_ok());
|
||||||
mem
|
mem
|
||||||
}
|
}
|
||||||
|
@ -133,62 +135,71 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate(&mut self, size: usize) -> EvalResult<'tcx, Pointer> {
|
pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return Ok(Pointer::zst_ptr());
|
return Ok(Pointer::zst_ptr());
|
||||||
}
|
}
|
||||||
|
// make sure we can offset the result pointer by the worst possible alignment
|
||||||
|
// this allows cheaply checking for alignment directly in the pointer
|
||||||
|
let least_aligned_size = size + align;
|
||||||
if self.memory_size - self.memory_usage < size as u64 {
|
if self.memory_size - self.memory_usage < size as u64 {
|
||||||
return Err(EvalError::OutOfMemory {
|
return Err(EvalError::OutOfMemory {
|
||||||
allocation_size: size as u64,
|
allocation_size: least_aligned_size as u64,
|
||||||
memory_size: self.memory_size,
|
memory_size: self.memory_size,
|
||||||
memory_usage: self.memory_usage,
|
memory_usage: self.memory_usage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.memory_usage += size as u64;
|
self.memory_usage += size as u64;
|
||||||
let alloc = Allocation {
|
let alloc = Allocation {
|
||||||
bytes: vec![0; size],
|
bytes: vec![0; least_aligned_size],
|
||||||
relocations: BTreeMap::new(),
|
relocations: BTreeMap::new(),
|
||||||
undef_mask: UndefMask::new(size),
|
undef_mask: UndefMask::new(least_aligned_size),
|
||||||
|
align: align,
|
||||||
};
|
};
|
||||||
let id = self.next_id;
|
let id = self.next_id;
|
||||||
self.next_id.0 += 1;
|
self.next_id.0 += 1;
|
||||||
self.alloc_map.insert(id, alloc);
|
self.alloc_map.insert(id, alloc);
|
||||||
Ok(Pointer {
|
Ok(Pointer {
|
||||||
alloc_id: id,
|
alloc_id: id,
|
||||||
offset: 0,
|
// offset by the alignment, so larger accesses will fail
|
||||||
|
offset: align,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(solson): Track which allocations were returned from __rust_allocate and report an error
|
// TODO(solson): Track which allocations were returned from __rust_allocate and report an error
|
||||||
// when reallocating/deallocating any others.
|
// when reallocating/deallocating any others.
|
||||||
pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<'tcx, Pointer> {
|
pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> EvalResult<'tcx, Pointer> {
|
||||||
if ptr.offset != 0 {
|
if ptr.offset != self.get(ptr.alloc_id)?.align {
|
||||||
// TODO(solson): Report error about non-__rust_allocate'd pointer.
|
// TODO(solson): Report error about non-__rust_allocate'd pointer.
|
||||||
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
|
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
|
||||||
}
|
}
|
||||||
if ptr.points_to_zst() {
|
if ptr.points_to_zst() {
|
||||||
return self.allocate(new_size);
|
return self.allocate(new_size, align);
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = self.get_mut(ptr.alloc_id)?.bytes.len();
|
let size = self.get(ptr.alloc_id)?.bytes.len();
|
||||||
|
let least_aligned_size = new_size + align;
|
||||||
|
|
||||||
if new_size > size {
|
if least_aligned_size > size {
|
||||||
let amount = new_size - size;
|
let amount = least_aligned_size - size;
|
||||||
self.memory_usage += amount as u64;
|
self.memory_usage += amount as u64;
|
||||||
let alloc = self.get_mut(ptr.alloc_id)?;
|
let alloc = self.get_mut(ptr.alloc_id)?;
|
||||||
alloc.bytes.extend(iter::repeat(0).take(amount));
|
alloc.bytes.extend(iter::repeat(0).take(amount));
|
||||||
alloc.undef_mask.grow(amount, false);
|
alloc.undef_mask.grow(amount, false);
|
||||||
} else if size > new_size {
|
} else if size > least_aligned_size {
|
||||||
// it's possible to cause miri to use arbitrary amounts of memory that aren't detectable
|
// it's possible to cause miri to use arbitrary amounts of memory that aren't detectable
|
||||||
// through the memory_usage value, by allocating a lot and reallocating to zero
|
// through the memory_usage value, by allocating a lot and reallocating to zero
|
||||||
self.memory_usage -= (size - new_size) as u64;
|
self.memory_usage -= (size - least_aligned_size) as u64;
|
||||||
self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?;
|
self.clear_relocations(ptr.offset(least_aligned_size as isize), size - least_aligned_size)?;
|
||||||
let alloc = self.get_mut(ptr.alloc_id)?;
|
let alloc = self.get_mut(ptr.alloc_id)?;
|
||||||
alloc.bytes.truncate(new_size);
|
alloc.bytes.truncate(least_aligned_size);
|
||||||
alloc.undef_mask.truncate(new_size);
|
alloc.undef_mask.truncate(least_aligned_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ptr)
|
Ok(Pointer {
|
||||||
|
alloc_id: ptr.alloc_id,
|
||||||
|
offset: align,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(solson): See comment on `reallocate`.
|
// TODO(solson): See comment on `reallocate`.
|
||||||
|
@ -196,7 +207,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
||||||
if ptr.points_to_zst() {
|
if ptr.points_to_zst() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if ptr.offset != 0 {
|
if ptr.offset != self.get(ptr.alloc_id)?.align {
|
||||||
// TODO(solson): Report error about non-__rust_allocate'd pointer.
|
// TODO(solson): Report error about non-__rust_allocate'd pointer.
|
||||||
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
|
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,6 @@ fn bar() {
|
||||||
assert_eq!(x, 6);
|
assert_eq!(x, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() { //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory
|
fn main() { //~ ERROR tried to allocate 8 more bytes, but only 0 bytes are free of the 0 byte memory
|
||||||
bar();
|
bar();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
fn bar(i: i32) {
|
fn bar(i: i32) {
|
||||||
if i < 1000 {
|
if i < 1000 {
|
||||||
bar(i + 1) //~ ERROR tried to allocate 4 more bytes, but only 1 bytes are free of the 1000 byte memory
|
bar(i + 1) //~ ERROR tried to allocate 8 more bytes, but only 1 bytes are free of the 1000 byte memory
|
||||||
//~^NOTE inside call to bar
|
//~^NOTE inside call to bar
|
||||||
//~|NOTE inside call to bar
|
//~|NOTE inside call to bar
|
||||||
//~|NOTE inside call to bar
|
//~|NOTE inside call to bar
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
let v: Vec<u8> = vec![1, 2];
|
let v: Vec<u8> = vec![1, 2];
|
||||||
let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 2
|
let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 3
|
||||||
panic!("this should never print: {}", x);
|
panic!("this should never print: {}", x);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
let v: Vec<u8> = vec![1, 2];
|
let v: Vec<u8> = vec![1, 2];
|
||||||
let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation
|
let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 6..7 outside bounds of allocation
|
||||||
panic!("this should never print: {}", x);
|
panic!("this should never print: {}", x);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue