CTFE validation: catch ReadPointerAsBytes and better error
This commit is contained in:
parent
dd4851d503
commit
d6dcb3de54
2 changed files with 30 additions and 14 deletions
|
@ -21,7 +21,7 @@ use std::hash::Hash;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
|
CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
|
||||||
ValueVisitor,
|
ScalarMaybeUninit, ValueVisitor,
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! throw_validation_failure {
|
macro_rules! throw_validation_failure {
|
||||||
|
@ -378,7 +378,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
value: OpTy<'tcx, M::PointerTag>,
|
value: OpTy<'tcx, M::PointerTag>,
|
||||||
kind: &str,
|
kind: &str,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
let value = self.ecx.read_immediate(value)?;
|
let value = try_validation!(
|
||||||
|
self.ecx.read_immediate(value),
|
||||||
|
self.path,
|
||||||
|
err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" },
|
||||||
|
);
|
||||||
// Handle wide pointers.
|
// Handle wide pointers.
|
||||||
// Check metadata early, for better diagnostics
|
// Check metadata early, for better diagnostics
|
||||||
let place = try_validation!(
|
let place = try_validation!(
|
||||||
|
@ -485,6 +489,17 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_scalar(
|
||||||
|
&self,
|
||||||
|
op: OpTy<'tcx, M::PointerTag>,
|
||||||
|
) -> InterpResult<'tcx, ScalarMaybeUninit<M::PointerTag>> {
|
||||||
|
Ok(try_validation!(
|
||||||
|
self.ecx.read_scalar(op),
|
||||||
|
self.path,
|
||||||
|
err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "plain (non-pointer) bytes" },
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if this is a value of primitive type, and if yes check the validity of the value
|
/// Check if this is a value of primitive type, and if yes check the validity of the value
|
||||||
/// at that type. Return `true` if the type is indeed primitive.
|
/// at that type. Return `true` if the type is indeed primitive.
|
||||||
fn try_visit_primitive(
|
fn try_visit_primitive(
|
||||||
|
@ -495,7 +510,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
let ty = value.layout.ty;
|
let ty = value.layout.ty;
|
||||||
match ty.kind() {
|
match ty.kind() {
|
||||||
ty::Bool => {
|
ty::Bool => {
|
||||||
let value = self.ecx.read_scalar(value)?;
|
let value = self.read_scalar(value)?;
|
||||||
try_validation!(
|
try_validation!(
|
||||||
value.to_bool(),
|
value.to_bool(),
|
||||||
self.path,
|
self.path,
|
||||||
|
@ -505,7 +520,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
ty::Char => {
|
ty::Char => {
|
||||||
let value = self.ecx.read_scalar(value)?;
|
let value = self.read_scalar(value)?;
|
||||||
try_validation!(
|
try_validation!(
|
||||||
value.to_char(),
|
value.to_char(),
|
||||||
self.path,
|
self.path,
|
||||||
|
@ -515,11 +530,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
|
ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
|
||||||
let value = try_validation!(
|
let value = self.read_scalar(value)?;
|
||||||
self.ecx.read_scalar(value),
|
|
||||||
self.path,
|
|
||||||
err_unsup!(ReadPointerAsBytes) => { "read of part of a pointer" },
|
|
||||||
);
|
|
||||||
// NOTE: Keep this in sync with the array optimization for int/float
|
// NOTE: Keep this in sync with the array optimization for int/float
|
||||||
// types below!
|
// types below!
|
||||||
if self.ctfe_mode.is_some() {
|
if self.ctfe_mode.is_some() {
|
||||||
|
@ -541,9 +552,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
// actually enforce the strict rules for raw pointers (mostly because
|
// actually enforce the strict rules for raw pointers (mostly because
|
||||||
// that lets us re-use `ref_to_mplace`).
|
// that lets us re-use `ref_to_mplace`).
|
||||||
let place = try_validation!(
|
let place = try_validation!(
|
||||||
self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?),
|
self.ecx.read_immediate(value).and_then(|i| self.ecx.ref_to_mplace(i)),
|
||||||
self.path,
|
self.path,
|
||||||
err_ub!(InvalidUninitBytes(None)) => { "uninitialized raw pointer" },
|
err_ub!(InvalidUninitBytes(None)) => { "uninitialized raw pointer" },
|
||||||
|
err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" },
|
||||||
);
|
);
|
||||||
if place.layout.is_unsized() {
|
if place.layout.is_unsized() {
|
||||||
self.check_wide_ptr_meta(place.meta, place.layout)?;
|
self.check_wide_ptr_meta(place.meta, place.layout)?;
|
||||||
|
@ -569,9 +581,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
ty::FnPtr(_sig) => {
|
ty::FnPtr(_sig) => {
|
||||||
let value = self.ecx.read_scalar(value)?;
|
let value = try_validation!(
|
||||||
|
self.ecx.read_immediate(value),
|
||||||
|
self.path,
|
||||||
|
err_unsup!(ReadPointerAsBytes) => { "part of a pointer" } expected { "a proper pointer or integer value" },
|
||||||
|
);
|
||||||
let _fn = try_validation!(
|
let _fn = try_validation!(
|
||||||
value.check_init().and_then(|ptr| self.ecx.memory.get_fn(ptr)),
|
value.to_scalar().and_then(|ptr| self.ecx.memory.get_fn(ptr)),
|
||||||
self.path,
|
self.path,
|
||||||
err_ub!(DanglingIntPointer(..)) |
|
err_ub!(DanglingIntPointer(..)) |
|
||||||
err_ub!(InvalidFunctionPointer(..)) |
|
err_ub!(InvalidFunctionPointer(..)) |
|
||||||
|
@ -615,7 +631,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
op: OpTy<'tcx, M::PointerTag>,
|
op: OpTy<'tcx, M::PointerTag>,
|
||||||
scalar_layout: &Scalar,
|
scalar_layout: &Scalar,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
let value = self.ecx.read_scalar(op)?;
|
let value = self.read_scalar(op)?;
|
||||||
let valid_range = &scalar_layout.valid_range;
|
let valid_range = &scalar_layout.valid_range;
|
||||||
let (lo, hi) = valid_range.clone().into_inner();
|
let (lo, hi) = valid_range.clone().into_inner();
|
||||||
// Determine the allowed range
|
// Determine the allowed range
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value
|
||||||
--> $DIR/issue-79690.rs:29:1
|
--> $DIR/issue-79690.rs:29:1
|
||||||
|
|
|
|
||||||
LL | const G: Fat = unsafe { Transmute { t: FOO }.u };
|
LL | const G: Fat = unsafe { Transmute { t: FOO }.u };
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered read of part of a pointer at .1.<deref>.size.foo
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered (potentially part of) a pointer at .1.<deref>.size.foo, but expected plain (non-pointer) bytes
|
||||||
|
|
|
|
||||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue