提交 e0320b5f 编写于 作者: R Ralf Jung

validation: port more checks to the pattern-based macro (and give it the shorter name)

上级 04689e22
...@@ -380,6 +380,8 @@ pub enum UndefinedBehaviorInfo { ...@@ -380,6 +380,8 @@ pub enum UndefinedBehaviorInfo {
InvalidDiscriminant(ScalarMaybeUndef), InvalidDiscriminant(ScalarMaybeUndef),
/// Using a pointer-not-to-a-function as function pointer. /// Using a pointer-not-to-a-function as function pointer.
InvalidFunctionPointer(Pointer), InvalidFunctionPointer(Pointer),
/// Using a string that is not valid UTF-8,
InvalidStr(std::str::Utf8Error),
/// Using uninitialized data where it is not allowed. /// Using uninitialized data where it is not allowed.
InvalidUndefBytes(Option<Pointer>), InvalidUndefBytes(Option<Pointer>),
/// Working with a local that is not currently live. /// Working with a local that is not currently live.
...@@ -446,6 +448,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ...@@ -446,6 +448,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
InvalidFunctionPointer(p) => { InvalidFunctionPointer(p) => {
write!(f, "using {} as function pointer but it does not point to a function", p) write!(f, "using {} as function pointer but it does not point to a function", p)
} }
InvalidStr(err) => {
write!(f, "this string is not valid UTF-8: {}", err)
}
InvalidUndefBytes(Some(p)) => write!( InvalidUndefBytes(Some(p)) => write!(
f, f,
"reading uninitialized memory at {}, but this operation requires initialized memory", "reading uninitialized memory at {}, but this operation requires initialized memory",
......
...@@ -328,7 +328,7 @@ pub fn read_str(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'t ...@@ -328,7 +328,7 @@ pub fn read_str(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'t
let len = mplace.len(self)?; let len = mplace.len(self)?;
let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?; let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?;
let str = ::std::str::from_utf8(bytes) let str = ::std::str::from_utf8(bytes)
.map_err(|err| err_ub_format!("this string is not valid UTF-8: {}", err))?; .map_err(|err| err_ub!(InvalidStr(err)))?;
Ok(str) Ok(str)
} }
......
...@@ -38,12 +38,12 @@ ...@@ -38,12 +38,12 @@
} }
/// Returns a validation failure for any Err value of $e. /// Returns a validation failure for any Err value of $e.
// FIXME: Replace all usages of try_validation! with try_validation_pat!. // FIXME: Replace all usages of try_validation_catchall! with try_validation!.
macro_rules! try_validation { macro_rules! try_validation_catchall {
($e:expr, $what:expr, $where:expr $(, $expected:expr )?) => {{ ($e:expr, $what:expr, $where:expr $(, $expected:expr )?) => {{
try_validation_pat!($e, $where, { try_validation!($e, $where,
_ => { "{}", $what } $( expected { "{}", $expected } )?, _ => { "{}", $what } $( expected { "{}", $expected } )?,
}) )
}}; }};
} }
/// Like try_validation, but will throw a validation error if any of the patterns in $p are /// Like try_validation, but will throw a validation error if any of the patterns in $p are
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
/// as a kind of validation blacklist: /// as a kind of validation blacklist:
/// ///
/// ``` /// ```
/// let v = try_validation_pat!(some_fn(), some_path, { /// let v = try_validation!(some_fn(), some_path, {
/// Foo | Bar | Baz => { "some failure" }, /// Foo | Bar | Baz => { "some failure" },
/// }); /// });
/// // Failures that match $p are thrown up as validation errors, but other errors are passed back /// // Failures that match $p are thrown up as validation errors, but other errors are passed back
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
/// An additional expected parameter can also be added to the failure message: /// An additional expected parameter can also be added to the failure message:
/// ///
/// ``` /// ```
/// let v = try_validation_pat!(some_fn(), some_path, { /// let v = try_validation!(some_fn(), some_path, {
/// Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" }, /// Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" },
/// }); /// });
/// ``` /// ```
...@@ -70,14 +70,15 @@ ...@@ -70,14 +70,15 @@
/// the format string in directly: /// the format string in directly:
/// ///
/// ``` /// ```
/// let v = try_validation_pat!(some_fn(), some_path, { /// let v = try_validation!(some_fn(), some_path, {
/// Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value }, /// Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value },
/// }); /// });
/// ``` /// ```
/// ///
macro_rules! try_validation_pat { macro_rules! try_validation {
($e:expr, $where:expr, { $( $p:pat )|+ => ($e:expr, $where:expr,
{ $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? $( , )?}) => {{ $( $p:pat )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? $( , )?
) => {{
match $e { match $e {
Ok(x) => x, Ok(x) => x,
// We catch the error and turn it into a validation failure. We are okay with // We catch the error and turn it into a validation failure. We are okay with
...@@ -303,21 +304,28 @@ fn check_wide_ptr_meta( ...@@ -303,21 +304,28 @@ fn check_wide_ptr_meta(
match tail.kind { match tail.kind {
ty::Dynamic(..) => { ty::Dynamic(..) => {
let vtable = meta.unwrap_meta(); let vtable = meta.unwrap_meta();
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
try_validation!( try_validation!(
self.ecx.memory.check_ptr_access( self.ecx.memory.check_ptr_access_align(
vtable, vtable,
3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align 3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align
self.ecx.tcx.data_layout.pointer_align.abi, Some(self.ecx.tcx.data_layout.pointer_align.abi),
CheckInAllocMsg::InboundsTest,
), ),
"dangling or unaligned vtable pointer in wide pointer or too small vtable", self.path,
self.path err_ub!(PointerOutOfBounds { .. }) |
err_ub!(AlignmentCheckFailed { .. }) |
err_ub!(DanglingIntPointer(..)) |
err_unsup!(ReadBytesAsPointer) => {
"dangling or unaligned vtable pointer in wide pointer or too small vtable"
},
); );
try_validation!( try_validation_catchall!(
self.ecx.read_drop_type_from_vtable(vtable), self.ecx.read_drop_type_from_vtable(vtable),
"invalid drop fn in vtable", "invalid drop fn in vtable",
self.path self.path
); );
try_validation!( try_validation_catchall!(
self.ecx.read_size_and_align_from_vtable(vtable), self.ecx.read_size_and_align_from_vtable(vtable),
"invalid size or align in vtable", "invalid size or align in vtable",
self.path self.path
...@@ -327,8 +335,8 @@ fn check_wide_ptr_meta( ...@@ -327,8 +335,8 @@ fn check_wide_ptr_meta(
ty::Slice(..) | ty::Str => { ty::Slice(..) | ty::Str => {
let _len = try_validation!( let _len = try_validation!(
meta.unwrap_meta().to_machine_usize(self.ecx), meta.unwrap_meta().to_machine_usize(self.ecx),
"non-integer slice length in wide pointer", self.path,
self.path err_unsup!(ReadPointerAsBytes) => { "non-integer slice length in wide pointer" },
); );
// We do not check that `len * elem_size <= isize::MAX`: // We do not check that `len * elem_size <= isize::MAX`:
// that is only required for references, and there it falls out of the // that is only required for references, and there it falls out of the
...@@ -354,8 +362,8 @@ fn check_safe_pointer( ...@@ -354,8 +362,8 @@ fn check_safe_pointer(
// Check metadata early, for better diagnostics // Check metadata early, for better diagnostics
let place = try_validation!( let place = try_validation!(
self.ecx.ref_to_mplace(value), self.ecx.ref_to_mplace(value),
format_args!("uninitialized {}", kind), self.path,
self.path err_ub!(InvalidUndefBytes(..)) => { "uninitialized {}", kind },
); );
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)?;
...@@ -376,6 +384,7 @@ fn check_safe_pointer( ...@@ -376,6 +384,7 @@ fn check_safe_pointer(
// alignment and size determined by the layout (size will be 0, // alignment and size determined by the layout (size will be 0,
// alignment should take attributes into account). // alignment should take attributes into account).
.unwrap_or_else(|| (place.layout.size, place.layout.align.abi)); .unwrap_or_else(|| (place.layout.size, place.layout.align.abi));
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
let ptr: Option<_> = match self.ecx.memory.check_ptr_access_align( let ptr: Option<_> = match self.ecx.memory.check_ptr_access_align(
place.ptr, place.ptr,
size, size,
...@@ -489,12 +498,20 @@ fn try_visit_primitive( ...@@ -489,12 +498,20 @@ fn try_visit_primitive(
match ty.kind { match ty.kind {
ty::Bool => { ty::Bool => {
let value = self.ecx.read_scalar(value)?; let value = self.ecx.read_scalar(value)?;
try_validation!(value.to_bool(), value, self.path, "a boolean"); try_validation!(
value.to_bool(),
self.path,
err_ub!(InvalidBool(..)) => { "{}", value } expected { "a boolean" },
);
Ok(true) Ok(true)
} }
ty::Char => { ty::Char => {
let value = self.ecx.read_scalar(value)?; let value = self.ecx.read_scalar(value)?;
try_validation!(value.to_char(), value, self.path, "a valid unicode codepoint"); try_validation!(
value.to_char(),
self.path,
err_ub!(InvalidChar(..)) => { "{}", value } expected { "a valid unicode codepoint" },
);
Ok(true) Ok(true)
} }
ty::Float(_) | ty::Int(_) | ty::Uint(_) => { ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
...@@ -521,9 +538,11 @@ fn try_visit_primitive( ...@@ -521,9 +538,11 @@ fn try_visit_primitive(
// We are conservative with undef for integers, but try to // We are conservative with undef for integers, but try to
// 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_pat!(self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?), self.path, { let place = try_validation!(
self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?),
self.path,
err_ub!(InvalidUndefBytes(..)) => { "uninitialized raw pointer" }, err_ub!(InvalidUndefBytes(..)) => { "uninitialized raw pointer" },
}); );
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)?;
} }
...@@ -539,7 +558,7 @@ fn try_visit_primitive( ...@@ -539,7 +558,7 @@ fn try_visit_primitive(
} }
ty::FnPtr(_sig) => { ty::FnPtr(_sig) => {
let value = self.ecx.read_scalar(value)?; let value = self.ecx.read_scalar(value)?;
let _fn = try_validation!( let _fn = try_validation_catchall!(
value.not_undef().and_then(|ptr| self.ecx.memory.get_fn(ptr)), value.not_undef().and_then(|ptr| self.ecx.memory.get_fn(ptr)),
value, value,
self.path, self.path,
...@@ -598,9 +617,9 @@ fn visit_scalar( ...@@ -598,9 +617,9 @@ fn visit_scalar(
// At least one value is excluded. Get the bits. // At least one value is excluded. Get the bits.
let value = try_validation!( let value = try_validation!(
value.not_undef(), value.not_undef(),
value,
self.path, self.path,
format_args!("something {}", wrapping_range_format(valid_range, max_hi),) err_ub!(InvalidUndefBytes(..)) => { "{}", value }
expected { "something {}", wrapping_range_format(valid_range, max_hi) },
); );
let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) { let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) {
Err(ptr) => { Err(ptr) => {
...@@ -761,8 +780,8 @@ fn visit_aggregate( ...@@ -761,8 +780,8 @@ fn visit_aggregate(
let mplace = op.assert_mem_place(self.ecx); // strings are never immediate let mplace = op.assert_mem_place(self.ecx); // strings are never immediate
try_validation!( try_validation!(
self.ecx.read_str(mplace), self.ecx.read_str(mplace),
"uninitialized or non-UTF-8 data in str", self.path,
self.path err_ub!(InvalidStr(..)) => { "uninitialized or non-UTF-8 data in str" },
); );
} }
ty::Array(tys, ..) | ty::Slice(tys) ty::Array(tys, ..) | ty::Slice(tys)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册