1
Fork 0

Turn incorrect vtable size/alignment errors into hard const-UB errors

They were "freeform const UB" error message, but could reach validation
and trigger ICEs there. We now catch them during validation to avoid
that.
This commit is contained in:
Rémy Rakic 2021-06-12 13:13:38 +02:00
parent 0f6ba39fd8
commit cae1918b29
3 changed files with 20 additions and 11 deletions

View file

@ -227,7 +227,11 @@ pub enum UndefinedBehaviorInfo<'tcx> {
/// Invalid metadata in a wide pointer (using `str` to avoid allocations). /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
InvalidMeta(&'static str), InvalidMeta(&'static str),
/// Invalid drop function in vtable. /// Invalid drop function in vtable.
InvalidDropFn(FnSig<'tcx>), InvalidVtableDropFn(FnSig<'tcx>),
/// Invalid size in a vtable: too large.
InvalidVtableSize,
/// Invalid alignment in a vtable: too large, or not a power of 2.
InvalidVtableAlignment(String),
/// Reading a C string that does not end within its allocation. /// Reading a C string that does not end within its allocation.
UnterminatedCString(Pointer), UnterminatedCString(Pointer),
/// Dereferencing a dangling pointer after it got freed. /// Dereferencing a dangling pointer after it got freed.
@ -287,11 +291,15 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
InvalidDropFn(sig) => write!( InvalidVtableDropFn(sig) => write!(
f, f,
"invalid drop function signature: got {}, expected exactly one argument which must be a pointer type", "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
sig sig
), ),
InvalidVtableSize => {
write!(f, "invalid vtable: size is bigger than largest supported object")
}
InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {}", msg),
UnterminatedCString(p) => write!( UnterminatedCString(p) => write!(
f, f,
"reading a null-terminated string starting at {} with no null found before end of allocation", "reading a null-terminated string starting at {} with no null found before end of allocation",

View file

@ -137,9 +137,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// The drop function takes `*mut T` where `T` is the type being dropped, so get that. // The drop function takes `*mut T` where `T` is the type being dropped, so get that.
let args = fn_sig.inputs(); let args = fn_sig.inputs();
if args.len() != 1 { if args.len() != 1 {
throw_ub!(InvalidDropFn(fn_sig)); throw_ub!(InvalidVtableDropFn(fn_sig));
} }
let ty = args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidDropFn(fn_sig)))?.ty; let ty =
args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidVtableDropFn(fn_sig)))?.ty;
Ok((drop_instance, ty)) Ok((drop_instance, ty))
} }
@ -158,14 +159,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let size = u64::try_from(self.force_bits(size, pointer_size)?).unwrap(); let size = u64::try_from(self.force_bits(size, pointer_size)?).unwrap();
let align = vtable.read_ptr_sized(pointer_size * 2)?.check_init()?; let align = vtable.read_ptr_sized(pointer_size * 2)?.check_init()?;
let align = u64::try_from(self.force_bits(align, pointer_size)?).unwrap(); let align = u64::try_from(self.force_bits(align, pointer_size)?).unwrap();
let align = Align::from_bytes(align) let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?;
.map_err(|e| err_ub_format!("invalid vtable: alignment {}", e))?;
if size >= self.tcx.data_layout.obj_size_bound() { if size >= self.tcx.data_layout.obj_size_bound() {
throw_ub_format!( throw_ub!(InvalidVtableSize);
"invalid vtable: \
size is bigger than largest supported object"
);
} }
Ok((Size::from_bytes(size), align)) Ok((Size::from_bytes(size), align))
} }

View file

@ -349,12 +349,16 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
err_ub!(InvalidFunctionPointer(..)) | err_ub!(InvalidFunctionPointer(..)) |
err_unsup!(ReadBytesAsPointer) => err_unsup!(ReadBytesAsPointer) =>
{ "invalid drop function pointer in vtable (not pointing to a function)" }, { "invalid drop function pointer in vtable (not pointing to a function)" },
err_ub!(InvalidDropFn(..)) => err_ub!(InvalidVtableDropFn(..)) =>
{ "invalid drop function pointer in vtable (function has incompatible signature)" }, { "invalid drop function pointer in vtable (function has incompatible signature)" },
); );
try_validation!( try_validation!(
self.ecx.read_size_and_align_from_vtable(vtable), self.ecx.read_size_and_align_from_vtable(vtable),
self.path, self.path,
err_ub!(InvalidVtableSize) =>
{ "invalid vtable: size is bigger than largest supported object" },
err_ub!(InvalidVtableAlignment(msg)) =>
{ "invalid vtable: alignment {}", msg },
err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" }, err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" },
); );
// FIXME: More checks for the vtable. // FIXME: More checks for the vtable.