Skip to content

Commit e0320b5

Browse files
committed
validation: port more checks to the pattern-based macro (and give it the shorter name)
1 parent 04689e2 commit e0320b5

File tree

3 files changed

+54
-30
lines changed

3 files changed

+54
-30
lines changed

src/librustc_middle/mir/interpret/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,8 @@ pub enum UndefinedBehaviorInfo {
380380
InvalidDiscriminant(ScalarMaybeUndef),
381381
/// Using a pointer-not-to-a-function as function pointer.
382382
InvalidFunctionPointer(Pointer),
383+
/// Using a string that is not valid UTF-8,
384+
InvalidStr(std::str::Utf8Error),
383385
/// Using uninitialized data where it is not allowed.
384386
InvalidUndefBytes(Option<Pointer>),
385387
/// Working with a local that is not currently live.
@@ -446,6 +448,9 @@ impl fmt::Display for UndefinedBehaviorInfo {
446448
InvalidFunctionPointer(p) => {
447449
write!(f, "using {} as function pointer but it does not point to a function", p)
448450
}
451+
InvalidStr(err) => {
452+
write!(f, "this string is not valid UTF-8: {}", err)
453+
}
449454
InvalidUndefBytes(Some(p)) => write!(
450455
f,
451456
"reading uninitialized memory at {}, but this operation requires initialized memory",

src/librustc_mir/interpret/operand.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
328328
let len = mplace.len(self)?;
329329
let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?;
330330
let str = ::std::str::from_utf8(bytes)
331-
.map_err(|err| err_ub_format!("this string is not valid UTF-8: {}", err))?;
331+
.map_err(|err| err_ub!(InvalidStr(err)))?;
332332
Ok(str)
333333
}
334334

src/librustc_mir/interpret/validity.rs

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,20 @@ macro_rules! throw_validation_failure {
3838
}
3939

4040
/// Returns a validation failure for any Err value of $e.
41-
// FIXME: Replace all usages of try_validation! with try_validation_pat!.
42-
macro_rules! try_validation {
41+
// FIXME: Replace all usages of try_validation_catchall! with try_validation!.
42+
macro_rules! try_validation_catchall {
4343
($e:expr, $what:expr, $where:expr $(, $expected:expr )?) => {{
44-
try_validation_pat!($e, $where, {
44+
try_validation!($e, $where,
4545
_ => { "{}", $what } $( expected { "{}", $expected } )?,
46-
})
46+
)
4747
}};
4848
}
4949
/// Like try_validation, but will throw a validation error if any of the patterns in $p are
5050
/// matched. Other errors are passed back to the caller, unchanged. This lets you use the patterns
5151
/// as a kind of validation blacklist:
5252
///
5353
/// ```
54-
/// let v = try_validation_pat!(some_fn(), some_path, {
54+
/// let v = try_validation!(some_fn(), some_path, {
5555
/// Foo | Bar | Baz => { "some failure" },
5656
/// });
5757
/// // Failures that match $p are thrown up as validation errors, but other errors are passed back
@@ -61,7 +61,7 @@ macro_rules! try_validation {
6161
/// An additional expected parameter can also be added to the failure message:
6262
///
6363
/// ```
64-
/// let v = try_validation_pat!(some_fn(), some_path, {
64+
/// let v = try_validation!(some_fn(), some_path, {
6565
/// Foo | Bar | Baz => { "some failure" } expected { "something that wasn't a failure" },
6666
/// });
6767
/// ```
@@ -70,14 +70,15 @@ macro_rules! try_validation {
7070
/// the format string in directly:
7171
///
7272
/// ```
73-
/// let v = try_validation_pat!(some_fn(), some_path, {
73+
/// let v = try_validation!(some_fn(), some_path, {
7474
/// Foo | Bar | Baz => { "{:?}", some_failure } expected { "{}", expected_value },
7575
/// });
7676
/// ```
7777
///
78-
macro_rules! try_validation_pat {
79-
($e:expr, $where:expr, { $( $p:pat )|+ =>
80-
{ $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? $( , )?}) => {{
78+
macro_rules! try_validation {
79+
($e:expr, $where:expr,
80+
$( $p:pat )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? $( , )?
81+
) => {{
8182
match $e {
8283
Ok(x) => x,
8384
// We catch the error and turn it into a validation failure. We are okay with
@@ -303,21 +304,28 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
303304
match tail.kind {
304305
ty::Dynamic(..) => {
305306
let vtable = meta.unwrap_meta();
307+
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
306308
try_validation!(
307-
self.ecx.memory.check_ptr_access(
309+
self.ecx.memory.check_ptr_access_align(
308310
vtable,
309311
3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align
310-
self.ecx.tcx.data_layout.pointer_align.abi,
312+
Some(self.ecx.tcx.data_layout.pointer_align.abi),
313+
CheckInAllocMsg::InboundsTest,
311314
),
312-
"dangling or unaligned vtable pointer in wide pointer or too small vtable",
313-
self.path
315+
self.path,
316+
err_ub!(PointerOutOfBounds { .. }) |
317+
err_ub!(AlignmentCheckFailed { .. }) |
318+
err_ub!(DanglingIntPointer(..)) |
319+
err_unsup!(ReadBytesAsPointer) => {
320+
"dangling or unaligned vtable pointer in wide pointer or too small vtable"
321+
},
314322
);
315-
try_validation!(
323+
try_validation_catchall!(
316324
self.ecx.read_drop_type_from_vtable(vtable),
317325
"invalid drop fn in vtable",
318326
self.path
319327
);
320-
try_validation!(
328+
try_validation_catchall!(
321329
self.ecx.read_size_and_align_from_vtable(vtable),
322330
"invalid size or align in vtable",
323331
self.path
@@ -327,8 +335,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
327335
ty::Slice(..) | ty::Str => {
328336
let _len = try_validation!(
329337
meta.unwrap_meta().to_machine_usize(self.ecx),
330-
"non-integer slice length in wide pointer",
331-
self.path
338+
self.path,
339+
err_unsup!(ReadPointerAsBytes) => { "non-integer slice length in wide pointer" },
332340
);
333341
// We do not check that `len * elem_size <= isize::MAX`:
334342
// that is only required for references, and there it falls out of the
@@ -354,8 +362,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
354362
// Check metadata early, for better diagnostics
355363
let place = try_validation!(
356364
self.ecx.ref_to_mplace(value),
357-
format_args!("uninitialized {}", kind),
358-
self.path
365+
self.path,
366+
err_ub!(InvalidUndefBytes(..)) => { "uninitialized {}", kind },
359367
);
360368
if place.layout.is_unsized() {
361369
self.check_wide_ptr_meta(place.meta, place.layout)?;
@@ -376,6 +384,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
376384
// alignment and size determined by the layout (size will be 0,
377385
// alignment should take attributes into account).
378386
.unwrap_or_else(|| (place.layout.size, place.layout.align.abi));
387+
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
379388
let ptr: Option<_> = match self.ecx.memory.check_ptr_access_align(
380389
place.ptr,
381390
size,
@@ -489,12 +498,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
489498
match ty.kind {
490499
ty::Bool => {
491500
let value = self.ecx.read_scalar(value)?;
492-
try_validation!(value.to_bool(), value, self.path, "a boolean");
501+
try_validation!(
502+
value.to_bool(),
503+
self.path,
504+
err_ub!(InvalidBool(..)) => { "{}", value } expected { "a boolean" },
505+
);
493506
Ok(true)
494507
}
495508
ty::Char => {
496509
let value = self.ecx.read_scalar(value)?;
497-
try_validation!(value.to_char(), value, self.path, "a valid unicode codepoint");
510+
try_validation!(
511+
value.to_char(),
512+
self.path,
513+
err_ub!(InvalidChar(..)) => { "{}", value } expected { "a valid unicode codepoint" },
514+
);
498515
Ok(true)
499516
}
500517
ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
@@ -521,9 +538,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
521538
// We are conservative with undef for integers, but try to
522539
// actually enforce the strict rules for raw pointers (mostly because
523540
// that lets us re-use `ref_to_mplace`).
524-
let place = try_validation_pat!(self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?), self.path, {
541+
let place = try_validation!(
542+
self.ecx.ref_to_mplace(self.ecx.read_immediate(value)?),
543+
self.path,
525544
err_ub!(InvalidUndefBytes(..)) => { "uninitialized raw pointer" },
526-
});
545+
);
527546
if place.layout.is_unsized() {
528547
self.check_wide_ptr_meta(place.meta, place.layout)?;
529548
}
@@ -539,7 +558,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
539558
}
540559
ty::FnPtr(_sig) => {
541560
let value = self.ecx.read_scalar(value)?;
542-
let _fn = try_validation!(
561+
let _fn = try_validation_catchall!(
543562
value.not_undef().and_then(|ptr| self.ecx.memory.get_fn(ptr)),
544563
value,
545564
self.path,
@@ -598,9 +617,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
598617
// At least one value is excluded. Get the bits.
599618
let value = try_validation!(
600619
value.not_undef(),
601-
value,
602620
self.path,
603-
format_args!("something {}", wrapping_range_format(valid_range, max_hi),)
621+
err_ub!(InvalidUndefBytes(..)) => { "{}", value }
622+
expected { "something {}", wrapping_range_format(valid_range, max_hi) },
604623
);
605624
let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) {
606625
Err(ptr) => {
@@ -761,8 +780,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
761780
let mplace = op.assert_mem_place(self.ecx); // strings are never immediate
762781
try_validation!(
763782
self.ecx.read_str(mplace),
764-
"uninitialized or non-UTF-8 data in str",
765-
self.path
783+
self.path,
784+
err_ub!(InvalidStr(..)) => { "uninitialized or non-UTF-8 data in str" },
766785
);
767786
}
768787
ty::Array(tys, ..) | ty::Slice(tys)

0 commit comments

Comments
 (0)