Skip to content

Commit b4e300d

Browse files
authored
Merge pull request #266 from RalfJung/align
Always test alignment in memory.rs
2 parents 308c7ca + 14cb858 commit b4e300d

File tree

5 files changed

+52
-16
lines changed

5 files changed

+52
-16
lines changed

src/memory.rs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
194194
}
195195

196196
let ptr = self.allocate(bytes.len() as u64, 1, Kind::UninitializedStatic)?;
197-
self.write_bytes(PrimVal::Ptr(ptr), bytes)?;
197+
self.write_bytes(ptr.into(), bytes)?;
198198
self.mark_static_initalized(ptr.alloc_id, Mutability::Immutable)?;
199199
self.literal_alloc_cache.insert(bytes.to_vec(), ptr.alloc_id);
200200
Ok(ptr)
@@ -280,6 +280,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
280280
self.layout.endian
281281
}
282282

283+
/// Check that the pointer is aligned and non-NULL
283284
pub fn check_align(&self, ptr: Pointer, align: u64) -> EvalResult<'tcx> {
284285
let offset = match ptr.into_inner_primval() {
285286
PrimVal::Ptr(ptr) => {
@@ -532,13 +533,13 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
532533
/// Byte accessors
533534
impl<'a, 'tcx> Memory<'a, 'tcx> {
534535
fn get_bytes_unchecked(&self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> {
535-
if size == 0 {
536-
return Ok(&[]);
537-
}
538-
// FIXME: check alignment for zst memory accesses?
536+
// Zero-sized accesses can use dangling pointers, but they still have to be aligned and non-NULL
539537
if self.reads_are_aligned {
540538
self.check_align(ptr.into(), align)?;
541539
}
540+
if size == 0 {
541+
return Ok(&[]);
542+
}
542543
self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
543544
let alloc = self.get(ptr.alloc_id)?;
544545
assert_eq!(ptr.offset as usize as u64, ptr.offset);
@@ -548,13 +549,13 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
548549
}
549550

550551
fn get_bytes_unchecked_mut(&mut self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> {
551-
if size == 0 {
552-
return Ok(&mut []);
553-
}
554-
// FIXME: check alignment for zst memory accesses?
552+
// Zero-sized accesses can use dangling pointers, but they still have to be aligned and non-NULL
555553
if self.writes_are_aligned {
556554
self.check_align(ptr.into(), align)?;
557555
}
556+
if size == 0 {
557+
return Ok(&mut []);
558+
}
558559
self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
559560
let alloc = self.get_mut(ptr.alloc_id)?;
560561
assert_eq!(ptr.offset as usize as u64, ptr.offset);
@@ -643,7 +644,13 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
643644

644645
pub fn copy(&mut self, src: Pointer, dest: Pointer, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> {
645646
if size == 0 {
646-
// TODO: Should we check for alignment here? (Also see write_bytes intrinsic)
647+
// Empty accesses don't need to be valid pointers, but they should still be aligned
648+
if self.reads_are_aligned {
649+
self.check_align(src, align)?;
650+
}
651+
if self.writes_are_aligned {
652+
self.check_align(dest, align)?;
653+
}
647654
return Ok(());
648655
}
649656
let src = src.to_ptr()?;
@@ -695,13 +702,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
695702

696703
pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> {
697704
if size == 0 {
705+
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
706+
if self.reads_are_aligned {
707+
self.check_align(ptr, 1)?;
708+
}
698709
return Ok(&[]);
699710
}
700711
self.get_bytes(ptr.to_ptr()?, size, 1)
701712
}
702713

703-
pub fn write_bytes(&mut self, ptr: PrimVal, src: &[u8]) -> EvalResult<'tcx> {
714+
pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx> {
704715
if src.is_empty() {
716+
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
717+
if self.writes_are_aligned {
718+
self.check_align(ptr, 1)?;
719+
}
705720
return Ok(());
706721
}
707722
let bytes = self.get_bytes_mut(ptr.to_ptr()?, src.len() as u64, 1)?;
@@ -711,6 +726,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
711726

712727
pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx> {
713728
if count == 0 {
729+
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
730+
if self.writes_are_aligned {
731+
self.check_align(ptr, 1)?;
732+
}
714733
return Ok(());
715734
}
716735
let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, 1)?;

src/terminator/intrinsic.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
143143
"copy_nonoverlapping" => {
144144
let elem_ty = substs.type_at(0);
145145
let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value");
146-
if elem_size != 0 {
146+
let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
147+
if count * elem_size != 0 {
148+
// TODO: We do not even validate alignment for the 0-bytes case. libstd relies on this in vec::IntoIter::next.
149+
// Also see the write_bytes intrinsic.
147150
let elem_align = self.type_align(elem_ty)?;
148151
let src = arg_vals[0].into_ptr(&mut self.memory)?;
149152
let dest = arg_vals[1].into_ptr(&mut self.memory)?;
150-
let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
151153
self.memory.copy(src, dest, count * elem_size, elem_align, intrinsic_name.ends_with("_nonoverlapping"))?;
152154
}
153155
}
@@ -465,7 +467,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
465467
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
466468
let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
467469
if count > 0 {
468-
// TODO: Should we, at least, validate the alignment? (Also see memory::copy)
470+
// HashMap relies on write_bytes on a NULL ptr with count == 0 to work
471+
// TODO: Should we, at least, validate the alignment? (Also see the copy intrinsic)
469472
self.memory.check_align(ptr, ty_align)?;
470473
self.memory.write_repeat(ptr, val_byte, size * count)?;
471474
}

src/terminator/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -814,8 +814,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
814814
if let Some((name, value)) = new {
815815
// +1 for the null terminator
816816
let value_copy = self.memory.allocate((value.len() + 1) as u64, 1, Kind::Env)?;
817-
self.memory.write_bytes(PrimVal::Ptr(value_copy), &value)?;
818-
self.memory.write_bytes(PrimVal::Ptr(value_copy.offset(value.len() as u64, self.memory.layout)?), &[0])?;
817+
self.memory.write_bytes(value_copy.into(), &value)?;
818+
self.memory.write_bytes(value_copy.offset(value.len() as u64, self.memory.layout)?.into(), &[0])?;
819819
if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) {
820820
self.memory.deallocate(var, None, Kind::Env)?;
821821
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn main() {
2+
let x = &2u16;
3+
let x = x as *const _ as *const [u32; 0];
4+
// This must fail because alignment is violated. Test specifically for loading ZST.
5+
let _x = unsafe { *x }; //~ ERROR: tried to access memory with alignment 2, but alignment 4 is required
6+
}

tests/run-pass-fullmir/vecs.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ fn vec_into_iter() -> u8 {
2424
.fold(0, |x, y| x + y)
2525
}
2626

27+
fn vec_into_iter_zst() -> usize {
28+
vec![[0u64; 0], [0u64; 0]]
29+
.into_iter()
30+
.map(|x| x.len())
31+
.sum()
32+
}
33+
2734
fn vec_reallocate() -> Vec<u8> {
2835
let mut v = vec![1, 2];
2936
v.push(3);
@@ -35,6 +42,7 @@ fn vec_reallocate() -> Vec<u8> {
3542
fn main() {
3643
assert_eq!(vec_reallocate().len(), 5);
3744
assert_eq!(vec_into_iter(), 30);
45+
assert_eq!(vec_into_iter_zst(), 0);
3846
assert_eq!(make_vec().capacity(), 4);
3947
assert_eq!(make_vec_macro(), [1, 2]);
4048
assert_eq!(make_vec_macro_repeat(), [42; 5]);

0 commit comments

Comments
 (0)