Skip to content

Commit 14d1309

Browse files
authored
Merge pull request rust-lang#210 from RalfJung/ptrcast
Change how pointer casts work
2 parents 86e2367 + 76a1d66 commit 14d1309

File tree

10 files changed

+80
-43
lines changed

10 files changed

+80
-43
lines changed

src/cast.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
1919
F32 => self.cast_float(val.to_f32()? as f64, dest_ty),
2020
F64 => self.cast_float(val.to_f64()?, dest_ty),
2121

22-
I8 | I16 | I32 | I64 | I128 => self.cast_signed_int(val.to_i128()?, dest_ty),
23-
24-
Bool | Char | U8 | U16 | U32 | U64 | U128 => self.cast_int(val.to_u128()?, dest_ty, false),
22+
I8 | I16 | I32 | I64 | I128 => {
23+
if val.is_ptr() {
24+
self.cast_ptr(val, dest_ty)
25+
} else {
26+
self.cast_signed_int(val.to_i128()?, dest_ty)
27+
}
28+
},
29+
30+
Bool | Char | U8 | U16 | U32 | U64 | U128 => {
31+
if val.is_ptr() {
32+
self.cast_ptr(val, dest_ty)
33+
} else {
34+
self.cast_int(val.to_u128()?, dest_ty, false)
35+
}
36+
},
2537

2638
FnPtr | Ptr => self.cast_ptr(val, dest_ty),
2739
}
@@ -70,6 +82,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
7082
TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
7183
TyChar => Err(EvalError::InvalidChar(v)),
7284

85+
// No alignment check needed for raw pointers
7386
TyRawPtr(_) => Ok(PrimVal::Bytes(v % (1 << self.memory.pointer_size()))),
7487

7588
_ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),
@@ -94,8 +107,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
94107
fn cast_ptr(&self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
95108
use rustc::ty::TypeVariants::*;
96109
match ty.sty {
97-
TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) =>
110+
// Casting to a reference or fn pointer is not permitted by rustc, no need to support it here.
111+
TyRawPtr(_) | TyInt(IntTy::Is) | TyUint(UintTy::Us) =>
98112
Ok(ptr),
113+
TyInt(_) | TyUint(_) => Err(EvalError::ReadPointerAsBytes),
99114
_ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
100115
}
101116
}

src/error.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub enum EvalError<'tcx> {
2121
access: bool,
2222
allocation_size: u64,
2323
},
24-
NullPointerOutOfBounds,
24+
InvalidNullPointerUsage,
2525
ReadPointerAsBytes,
2626
ReadBytesAsPointer,
2727
InvalidPointerMath,
@@ -84,8 +84,8 @@ impl<'tcx> Error for EvalError<'tcx> {
8484
"invalid enum discriminant value read",
8585
EvalError::PointerOutOfBounds { .. } =>
8686
"pointer offset outside bounds of allocation",
87-
EvalError::NullPointerOutOfBounds =>
88-
"invalid NULL pointer offset",
87+
EvalError::InvalidNullPointerUsage =>
88+
"invalid use of NULL pointer",
8989
EvalError::ReadPointerAsBytes =>
9090
"a raw memory access tried to access part of a pointer value as raw bytes",
9191
EvalError::ReadBytesAsPointer =>

src/eval_context.rs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
664664
Ref(_, _, ref lvalue) => {
665665
let src = self.eval_lvalue(lvalue)?;
666666
let (ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra();
667+
let ty = self.lvalue_ty(lvalue);
667668

668669
let val = match extra {
669670
LvalueExtra::None => Value::ByVal(ptr),
@@ -673,6 +674,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
673674
bug!("attempted to take a reference to an enum downcast lvalue"),
674675
};
675676

677+
// Check alignment and non-NULLness.
678+
let (_, align) = self.size_and_align_of_dst(ty, val)?;
679+
match ptr {
680+
PrimVal::Ptr(ptr) => {
681+
self.memory.check_align(ptr, align, 0)?;
682+
}
683+
PrimVal::Bytes(bytes) => {
684+
let v = ((bytes as u128) % (1 << self.memory.pointer_size())) as u64;
685+
if v == 0 {
686+
return Err(EvalError::InvalidNullPointerUsage);
687+
}
688+
if v % align != 0 {
689+
return Err(EvalError::AlignmentCheckFailed {
690+
has: v % align,
691+
required: align,
692+
});
693+
}
694+
}
695+
PrimVal::Undef => {
696+
return Err(EvalError::ReadUndefBytes);
697+
}
698+
}
699+
676700
self.write_value(val, dest, dest_ty)?;
677701
}
678702

@@ -717,19 +741,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
717741
(Value::ByVal(_), _) => bug!("expected fat ptr"),
718742
}
719743
} else {
720-
// First, try casting
721-
let dest_val = self.value_to_primval(src, src_ty).and_then(
722-
|src_val| { self.cast_primval(src_val, src_ty, dest_ty) })
723-
// Alternatively, if the sizes are equal, try just reading at the target type
724-
.or_else(|err| {
725-
let size = self.type_size(src_ty)?;
726-
if size.is_some() && size == self.type_size(dest_ty)? {
727-
self.value_to_primval(src, dest_ty)
728-
} else {
729-
Err(err)
730-
}
731-
});
732-
self.write_value(Value::ByVal(dest_val?), dest, dest_ty)?;
744+
let src_val = self.value_to_primval(src, src_ty)?;
745+
let dest_val = self.cast_primval(src_val, src_ty, dest_ty)?;
746+
self.write_value(Value::ByVal(dest_val), dest, dest_ty)?;
733747
}
734748
}
735749

@@ -908,7 +922,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
908922
// allocation.
909923

910924
if ptr.is_null()? { // NULL pointers must only be offset by 0
911-
return if offset == 0 { Ok(ptr) } else { Err(EvalError::NullPointerOutOfBounds) };
925+
return if offset == 0 { Ok(ptr) } else { Err(EvalError::InvalidNullPointerUsage) };
912926
}
913927
// FIXME: assuming here that type size is < i64::max_value()
914928
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
@@ -919,7 +933,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
919933
self.memory.check_bounds(ptr, false)?;
920934
} else if ptr.is_null()? {
921935
// We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now.
922-
return Err(EvalError::NullPointerOutOfBounds);
936+
return Err(EvalError::InvalidNullPointerUsage);
923937
}
924938
Ok(ptr)
925939
} else {

tests/compile-fail/int_ptr_cast.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
let x = 2usize as *const u32;
3+
// This must fail because alignment is violated
4+
let _ = unsafe { &*x }; //~ ERROR: tried to access memory with alignment 2, but alignment 4 is required
5+
}

tests/compile-fail/int_ptr_cast2.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
let x = 0usize as *const u32;
3+
// This must fail because the pointer is NULL
4+
let _ = unsafe { &*x }; //~ ERROR: invalid use of NULL pointer
5+
}

tests/compile-fail/ptr_int_cast.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
let x = &1;
3+
// Casting down to u8 and back up to a pointer loses too much precision; this must not work.
4+
let x = x as *const i32;
5+
let x = x as u8; //~ ERROR: a raw memory access tried to access part of a pointer value as raw bytes
6+
let x = x as *const i32;
7+
let _ = unsafe { *x };
8+
}

tests/compile-fail/reference_to_packed.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ fn main() {
1111
x: 42,
1212
y: 99,
1313
};
14-
let p = &foo.x;
15-
let i = *p; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required
16-
}
14+
let p = &foo.x; //~ ERROR tried to access memory with alignment 1, but alignment 4 is required
15+
let i = *p;
16+
}

tests/compile-fail/reference_to_packed_unsafe.rs

Lines changed: 0 additions & 16 deletions
This file was deleted.
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;
4+
// This must fail because alignment is violated
5+
let _ = unsafe { &*x }; //~ ERROR: tried to access memory with alignment 2, but alignment 4 is required
6+
}

tests/run-pass/ptr_int_casts.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn main() {
1313

1414
{ // ptr-int-ptr
1515
let x = 13;
16-
let mut y = &x as *const _ as usize;
16+
let mut y = &x as &_ as *const _ as usize;
1717
y += 13;
1818
y -= 13;
1919
let y = y as *const _;
@@ -22,7 +22,7 @@ fn main() {
2222

2323
{ // fnptr-int-fnptr
2424
let x : fn() -> i32 = f;
25-
let y : *mut u8 = unsafe { mem::transmute(x) };
25+
let y : *mut u8 = unsafe { mem::transmute(x as fn() -> i32) };
2626
let mut y = y as usize;
2727
y += 13;
2828
y -= 13;

0 commit comments

Comments
 (0)