Skip to content

Commit 58a4639

Browse files
authored
Merge pull request rust-lang#185 from RalfJung/pointers
Various pointer-related things
2 parents c16f24b + 91b93bc commit 58a4639

27 files changed

+322
-102
lines changed

src/error.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ pub enum EvalError<'tcx> {
1818
InvalidDiscriminant,
1919
PointerOutOfBounds {
2020
ptr: Pointer,
21-
size: u64,
21+
access: bool,
2222
allocation_size: u64,
2323
},
2424
ReadPointerAsBytes,
2525
InvalidPointerMath,
26+
OverflowingPointerMath,
2627
ReadUndefBytes,
2728
DeadLocal,
2829
InvalidBoolOp(mir::BinOp),
@@ -82,6 +83,8 @@ impl<'tcx> Error for EvalError<'tcx> {
8283
"a raw memory access tried to access part of a pointer value as raw bytes",
8384
EvalError::InvalidPointerMath =>
8485
"attempted to do math or a comparison on pointers into different allocations",
86+
EvalError::OverflowingPointerMath =>
87+
"attempted to do overflowing math on a pointer",
8588
EvalError::ReadUndefBytes =>
8689
"attempted to read undefined bytes",
8790
EvalError::DeadLocal =>
@@ -147,9 +150,10 @@ impl<'tcx> Error for EvalError<'tcx> {
147150
impl<'tcx> fmt::Display for EvalError<'tcx> {
148151
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149152
match *self {
150-
EvalError::PointerOutOfBounds { ptr, size, allocation_size } => {
151-
write!(f, "memory access of {}..{} outside bounds of allocation {} which has size {}",
152-
ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size)
153+
EvalError::PointerOutOfBounds { ptr, access, allocation_size } => {
154+
write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}",
155+
if access { "memory access" } else { "pointer computed" },
156+
ptr.offset, ptr.alloc_id, allocation_size)
153157
},
154158
EvalError::NoMirFor(ref func) => write!(f, "no mir for `{}`", func),
155159
EvalError::FunctionPointerTyMismatch(sig, got) =>

src/eval_context.rs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
391391
// FIXME(solson)
392392
let dest_ptr = self.force_allocation(dest)?.to_ptr();
393393

394-
let discr_dest = dest_ptr.offset(discr_offset);
394+
let discr_dest = dest_ptr.offset(discr_offset, self.memory.layout)?;
395395
self.memory.write_uint(discr_dest, discr_val, discr_size)?;
396396

397397
let dest = Lvalue::Ptr {
@@ -550,7 +550,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
550550
// FIXME(solson)
551551
let dest = self.force_allocation(dest)?.to_ptr();
552552

553-
let dest = dest.offset(offset.bytes());
553+
let dest = dest.offset(offset.bytes(), self.memory.layout)?;
554554
let dest_size = self.type_size(ty)?
555555
.expect("bad StructWrappedNullablePointer discrfield");
556556
self.memory.write_int(dest, 0, dest_size)?;
@@ -610,7 +610,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
610610
let dest = self.force_allocation(dest)?.to_ptr();
611611

612612
for i in 0..length {
613-
let elem_dest = dest.offset(i * elem_size);
613+
let elem_dest = dest.offset(i * elem_size, self.memory.layout)?;
614614
self.write_value_to_ptr(value, elem_dest, elem_ty)?;
615615
}
616616
}
@@ -662,7 +662,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
662662
let src = self.eval_operand(operand)?;
663663
let src_ty = self.operand_ty(operand);
664664
if self.type_is_fat_ptr(src_ty) {
665-
trace!("misc cast: {:?}", src);
666665
match (src, self.type_is_fat_ptr(dest_ty)) {
667666
(Value::ByRef(_), _) |
668667
(Value::ByValPair(..), true) => {
@@ -674,9 +673,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
674673
(Value::ByVal(_), _) => bug!("expected fat ptr"),
675674
}
676675
} else {
677-
let src_val = self.value_to_primval(src, src_ty)?;
678-
let dest_val = self.cast_primval(src_val, src_ty, dest_ty)?;
679-
self.write_value(Value::ByVal(dest_val), dest, dest_ty)?;
676+
// First, try casting
677+
let dest_val = self.value_to_primval(src, src_ty).and_then(
678+
|src_val| { self.cast_primval(src_val, src_ty, dest_ty) })
679+
// Alternatively, if the sizes are equal, try just reading at the target type
680+
.or_else(|err| {
681+
let size = self.type_size(src_ty)?;
682+
if size.is_some() && size == self.type_size(dest_ty)? {
683+
self.value_to_primval(src, dest_ty)
684+
} else {
685+
Err(err)
686+
}
687+
});
688+
self.write_value(Value::ByVal(dest_val?), dest, dest_ty)?;
680689
}
681690
}
682691

@@ -841,11 +850,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
841850
}
842851
}
843852

853+
pub(super) fn wrapping_pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> {
854+
// FIXME: assuming here that type size is < i64::max_value()
855+
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
856+
let offset = offset.overflowing_mul(pointee_size).0;
857+
Ok(ptr.wrapping_signed_offset(offset, self.memory.layout))
858+
}
859+
844860
pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> {
861+
if offset == 0 {
862+
// rustc relies on Offset-by-0 to be well-defined even for "bad" pointers like Unique::empty().
863+
return Ok(ptr);
864+
}
845865
// FIXME: assuming here that type size is < i64::max_value()
846866
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
847-
// FIXME: Check overflow, out-of-bounds
848-
Ok(ptr.signed_offset(offset * pointee_size))
867+
return if let Some(offset) = offset.checked_mul(pointee_size) {
868+
let ptr = ptr.signed_offset(offset, self.memory.layout)?;
869+
self.memory.check_bounds(ptr, false)?;
870+
Ok(ptr)
871+
} else {
872+
Err(EvalError::OverflowingPointerMath)
873+
}
849874
}
850875

851876
pub(super) fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> {
@@ -1099,8 +1124,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
10991124
let field_1_ty = self.get_field_ty(ty, 1)?;
11001125
let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized");
11011126
let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized");
1102-
self.memory.write_primval(ptr.offset(field_0), a, field_0_size)?;
1103-
self.memory.write_primval(ptr.offset(field_1), b, field_1_size)?;
1127+
self.memory.write_primval(ptr.offset(field_0, self.memory.layout)?, a, field_0_size)?;
1128+
self.memory.write_primval(ptr.offset(field_1, self.memory.layout)?, b, field_1_size)?;
11041129
Ok(())
11051130
}
11061131

@@ -1217,7 +1242,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
12171242
Ok(Value::ByVal(PrimVal::Ptr(p)))
12181243
} else {
12191244
trace!("reading fat pointer extra of type {}", pointee_ty);
1220-
let extra = ptr.offset(self.memory.pointer_size());
1245+
let extra = ptr.offset(self.memory.pointer_size(), self.memory.layout)?;
12211246
let extra = match self.tcx.struct_tail(pointee_ty).sty {
12221247
ty::TyDynamic(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?),
12231248
ty::TySlice(..) |
@@ -1402,8 +1427,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
14021427
}
14031428
let src_field_offset = self.get_field_offset(src_ty, i)?.bytes();
14041429
let dst_field_offset = self.get_field_offset(dest_ty, i)?.bytes();
1405-
let src_f_ptr = src_ptr.offset(src_field_offset);
1406-
let dst_f_ptr = dest.offset(dst_field_offset);
1430+
let src_f_ptr = src_ptr.offset(src_field_offset, self.memory.layout)?;
1431+
let dst_f_ptr = dest.offset(dst_field_offset, self.memory.layout)?;
14071432
if src_fty == dst_fty {
14081433
self.copy(src_f_ptr, dst_f_ptr, src_fty)?;
14091434
} else {
@@ -1438,6 +1463,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
14381463
panic!("Failed to access local: {:?}", err);
14391464
}
14401465
Ok(Value::ByRef(ptr)) => {
1466+
write!(msg, " by ref:").unwrap();
14411467
allocs.push(ptr.alloc_id);
14421468
}
14431469
Ok(Value::ByVal(val)) => {

src/lvalue.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
270270
_ => offset.bytes(),
271271
};
272272

273-
let ptr = base_ptr.offset(offset);
273+
let ptr = base_ptr.offset(offset, self.memory.layout)?;
274274

275275
let field_ty = self.monomorphize(field_ty, self.substs());
276276

@@ -363,7 +363,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
363363
let usize = self.tcx.types.usize;
364364
let n = self.value_to_primval(n_ptr, usize)?.to_u64()?;
365365
assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len);
366-
let ptr = base_ptr.offset(n * elem_size);
366+
let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?;
367367
(ptr, LvalueExtra::None)
368368
}
369369

@@ -384,7 +384,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
384384
u64::from(offset)
385385
};
386386

387-
let ptr = base_ptr.offset(index * elem_size);
387+
let ptr = base_ptr.offset(index * elem_size, self.memory.layout)?;
388388
(ptr, LvalueExtra::None)
389389
}
390390

@@ -398,7 +398,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
398398
let (elem_ty, n) = base.elem_ty_and_len(base_ty);
399399
let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
400400
assert!(u64::from(from) <= n - u64::from(to));
401-
let ptr = base_ptr.offset(u64::from(from) * elem_size);
401+
let ptr = base_ptr.offset(u64::from(from) * elem_size, self.memory.layout)?;
402402
let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from));
403403
(ptr, extra)
404404
}

src/memory.rs

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,36 @@ impl Pointer {
6060
Pointer { alloc_id, offset }
6161
}
6262

63-
pub fn signed_offset(self, i: i64) -> Self {
63+
pub fn wrapping_signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> Self {
64+
Pointer::new(self.alloc_id, (self.offset.wrapping_add(i as u64) as u128 % (1u128 << layout.pointer_size.bits())) as u64)
65+
}
66+
67+
pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> {
6468
// FIXME: is it possible to over/underflow here?
6569
if i < 0 {
6670
// trickery to ensure that i64::min_value() works fine
6771
// this formula only works for true negative values, it panics for zero!
6872
let n = u64::max_value() - (i as u64) + 1;
69-
Pointer::new(self.alloc_id, self.offset - n)
73+
if let Some(res) = self.offset.checked_sub(n) {
74+
Ok(Pointer::new(self.alloc_id, res))
75+
} else {
76+
Err(EvalError::OverflowingPointerMath)
77+
}
7078
} else {
71-
self.offset(i as u64)
79+
self.offset(i as u64, layout)
7280
}
7381
}
7482

75-
pub fn offset(self, i: u64) -> Self {
76-
Pointer::new(self.alloc_id, self.offset + i)
83+
pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> {
84+
if let Some(res) = self.offset.checked_add(i) {
85+
if res as u128 >= (1u128 << layout.pointer_size.bits()) {
86+
Err(EvalError::OverflowingPointerMath)
87+
} else {
88+
Ok(Pointer::new(self.alloc_id, res))
89+
}
90+
} else {
91+
Err(EvalError::OverflowingPointerMath)
92+
}
7793
}
7894

7995
pub fn points_to_zst(&self) -> bool {
@@ -108,7 +124,7 @@ pub type TlsKey = usize;
108124

109125
#[derive(Copy, Clone, Debug)]
110126
pub struct TlsEntry<'tcx> {
111-
data: Pointer, // will eventually become a map from thread IDs to pointers
127+
data: Pointer, // Will eventually become a map from thread IDs to pointers, if we ever support more than one thread.
112128
dtor: Option<ty::Instance<'tcx>>,
113129
}
114130

@@ -161,8 +177,8 @@ pub struct Memory<'a, 'tcx> {
161177
/// A cache for basic byte allocations keyed by their contents. This is used to deduplicate
162178
/// allocations for string and bytestring literals.
163179
literal_alloc_cache: HashMap<Vec<u8>, AllocId>,
164-
165-
/// pthreads-style Thread-local storage. We only have one thread, so this is just a map from TLS keys (indices into the vector) to the pointer stored there.
180+
181+
/// pthreads-style thread-local storage.
166182
thread_local: HashMap<TlsKey, TlsEntry<'tcx>>,
167183

168184
/// The Key to use for the next thread-local allocation.
@@ -271,7 +287,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
271287
alloc.undef_mask.grow(amount, false);
272288
} else if size > new_size {
273289
self.memory_usage -= size - new_size;
274-
self.clear_relocations(ptr.offset(new_size), size - new_size)?;
290+
self.clear_relocations(ptr.offset(new_size, self.layout)?, size - new_size)?;
275291
let alloc = self.get_mut(ptr.alloc_id)?;
276292
// `as usize` is fine here, since it is smaller than `size`, which came from a usize
277293
alloc.bytes.truncate(new_size as usize);
@@ -354,6 +370,15 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
354370
}
355371
}
356372

373+
pub(crate) fn check_bounds(&self, ptr: Pointer, access: bool) -> EvalResult<'tcx> {
374+
let alloc = self.get(ptr.alloc_id)?;
375+
let allocation_size = alloc.bytes.len() as u64;
376+
if ptr.offset > allocation_size {
377+
return Err(EvalError::PointerOutOfBounds { ptr, access, allocation_size });
378+
}
379+
Ok(())
380+
}
381+
357382
pub(crate) fn mark_packed(&mut self, ptr: Pointer, len: u64) {
358383
self.packed.insert(Entry {
359384
alloc_id: ptr.alloc_id,
@@ -574,11 +599,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
574599
return Ok(&[]);
575600
}
576601
self.check_align(ptr, align, size)?;
602+
self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
577603
let alloc = self.get(ptr.alloc_id)?;
578-
let allocation_size = alloc.bytes.len() as u64;
579-
if ptr.offset + size > allocation_size {
580-
return Err(EvalError::PointerOutOfBounds { ptr, size, allocation_size });
581-
}
582604
assert_eq!(ptr.offset as usize as u64, ptr.offset);
583605
assert_eq!(size as usize as u64, size);
584606
let offset = ptr.offset as usize;
@@ -590,11 +612,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
590612
return Ok(&mut []);
591613
}
592614
self.check_align(ptr, align, size)?;
615+
self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
593616
let alloc = self.get_mut(ptr.alloc_id)?;
594-
let allocation_size = alloc.bytes.len() as u64;
595-
if ptr.offset + size > allocation_size {
596-
return Err(EvalError::PointerOutOfBounds { ptr, size, allocation_size });
597-
}
598617
assert_eq!(ptr.offset as usize as u64, ptr.offset);
599618
assert_eq!(size as usize as u64, size);
600619
let offset = ptr.offset as usize;
@@ -746,7 +765,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
746765

747766
pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx> {
748767
self.write_usize(dest, ptr.offset as u64)?;
749-
self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id);
768+
if ptr.alloc_id != NEVER_ALLOC_ID {
769+
self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id);
770+
}
750771
Ok(())
751772
}
752773

@@ -913,7 +934,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
913934

914935
fn check_relocation_edges(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx> {
915936
let overlapping_start = self.relocations(ptr, 0)?.count();
916-
let overlapping_end = self.relocations(ptr.offset(size), 0)?.count();
937+
let overlapping_end = self.relocations(ptr.offset(size, self.layout)?, 0)?.count();
917938
if overlapping_start + overlapping_end != 0 {
918939
return Err(EvalError::ReadPointerAsBytes);
919940
}

0 commit comments

Comments
 (0)