Skip to content

Commit fb3ea63

Browse files
committed
Auto merge of #86245 - lqd:const-ub-align, r=RalfJung
Fix ICEs on invalid vtable size/alignment const UB errors The invalid vtable size/alignment errors from `InterpCx::read_size_and_align_from_vtable` were "freeform const UB errors", causing ICEs when reaching validation. This PR turns them into const UB hard errors to catch them during validation and avoid that. Fixes #86193 r? `@RalfJung` (It seemed cleaner to have 2 variants but they can be merged into one variant with a message payload if you prefer that ?)
2 parents 6cc5d54 + e29f3e8 commit fb3ea63

File tree

7 files changed

+149
-38
lines changed

7 files changed

+149
-38
lines changed

compiler/rustc_middle/src/mir/interpret/error.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,11 @@ pub enum UndefinedBehaviorInfo<'tcx> {
227227
/// Invalid metadata in a wide pointer (using `str` to avoid allocations).
228228
InvalidMeta(&'static str),
229229
/// Invalid drop function in vtable.
230-
InvalidDropFn(FnSig<'tcx>),
230+
InvalidVtableDropFn(FnSig<'tcx>),
231+
/// Invalid size in a vtable: too large.
232+
InvalidVtableSize,
233+
/// Invalid alignment in a vtable: too large, or not a power of 2.
234+
InvalidVtableAlignment(String),
231235
/// Reading a C string that does not end within its allocation.
232236
UnterminatedCString(Pointer),
233237
/// Dereferencing a dangling pointer after it got freed.
@@ -287,11 +291,15 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
287291
RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
288292
PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
289293
InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg),
290-
InvalidDropFn(sig) => write!(
294+
InvalidVtableDropFn(sig) => write!(
291295
f,
292296
"invalid drop function signature: got {}, expected exactly one argument which must be a pointer type",
293297
sig
294298
),
299+
InvalidVtableSize => {
300+
write!(f, "invalid vtable: size is bigger than largest supported object")
301+
}
302+
InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {}", msg),
295303
UnterminatedCString(p) => write!(
296304
f,
297305
"reading a null-terminated string starting at {} with no null found before end of allocation",

compiler/rustc_mir/src/interpret/traits.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
137137
// The drop function takes `*mut T` where `T` is the type being dropped, so get that.
138138
let args = fn_sig.inputs();
139139
if args.len() != 1 {
140-
throw_ub!(InvalidDropFn(fn_sig));
140+
throw_ub!(InvalidVtableDropFn(fn_sig));
141141
}
142-
let ty = args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidDropFn(fn_sig)))?.ty;
142+
let ty =
143+
args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidVtableDropFn(fn_sig)))?.ty;
143144
Ok((drop_instance, ty))
144145
}
145146

@@ -158,14 +159,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
158159
let size = u64::try_from(self.force_bits(size, pointer_size)?).unwrap();
159160
let align = vtable.read_ptr_sized(pointer_size * 2)?.check_init()?;
160161
let align = u64::try_from(self.force_bits(align, pointer_size)?).unwrap();
161-
let align = Align::from_bytes(align)
162-
.map_err(|e| err_ub_format!("invalid vtable: alignment {}", e))?;
162+
let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?;
163163

164164
if size >= self.tcx.data_layout.obj_size_bound() {
165-
throw_ub_format!(
166-
"invalid vtable: \
167-
size is bigger than largest supported object"
168-
);
165+
throw_ub!(InvalidVtableSize);
169166
}
170167
Ok((Size::from_bytes(size), align))
171168
}

compiler/rustc_mir/src/interpret/validity.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -349,12 +349,16 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
349349
err_ub!(InvalidFunctionPointer(..)) |
350350
err_unsup!(ReadBytesAsPointer) =>
351351
{ "invalid drop function pointer in vtable (not pointing to a function)" },
352-
err_ub!(InvalidDropFn(..)) =>
352+
err_ub!(InvalidVtableDropFn(..)) =>
353353
{ "invalid drop function pointer in vtable (function has incompatible signature)" },
354354
);
355355
try_validation!(
356356
self.ecx.read_size_and_align_from_vtable(vtable),
357357
self.path,
358+
err_ub!(InvalidVtableSize) =>
359+
{ "invalid vtable: size is bigger than largest supported object" },
360+
err_ub!(InvalidVtableAlignment(msg)) =>
361+
{ "invalid vtable: alignment {}", msg },
358362
err_unsup!(ReadPointerAsBytes) => { "invalid size or align in vtable" },
359363
);
360364
// FIXME: More checks for the vtable.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: any use of this value will cause an error
2+
--> $DIR/ub-incorrect-vtable.rs:19:14
3+
|
4+
LL | / const INVALID_VTABLE_ALIGNMENT: &dyn Trait =
5+
LL | | unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) };
6+
| |______________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^__-
7+
| |
8+
| invalid vtable: alignment `1000` is not a power of 2
9+
|
10+
= note: `#[deny(const_err)]` on by default
11+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
12+
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
13+
14+
error: any use of this value will cause an error
15+
--> $DIR/ub-incorrect-vtable.rs:25:14
16+
|
17+
LL | / const INVALID_VTABLE_SIZE: &dyn Trait =
18+
LL | | unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) };
19+
| |______________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^__-
20+
| |
21+
| invalid vtable: size is bigger than largest supported object
22+
|
23+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
24+
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
25+
26+
error[E0080]: it is undefined behavior to use this value
27+
--> $DIR/ub-incorrect-vtable.rs:36:1
28+
|
29+
LL | / const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
30+
LL | | unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), 1usize, 1000usize))) };
31+
| |_____________________________________________________________________________________________^ type validation failed: encountered invalid vtable: alignment `1000` is not a power of 2 at .0
32+
|
33+
= 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.
34+
= note: the raw bytes of the constant (size: 8, align: 4) {
35+
╾─allocN─╼ ╾─allocN─╼ │ ╾──╼╾──╼
36+
}
37+
38+
error[E0080]: it is undefined behavior to use this value
39+
--> $DIR/ub-incorrect-vtable.rs:41:1
40+
|
41+
LL | / const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
42+
LL | | unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), usize::MAX, 1usize))) };
43+
| |______________________________________________________________________________________________^ type validation failed: encountered invalid vtable: size is bigger than largest supported object at .0
44+
|
45+
= 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.
46+
= note: the raw bytes of the constant (size: 8, align: 4) {
47+
╾─allocN─╼ ╾─allocN─╼ │ ╾──╼╾──╼
48+
}
49+
50+
error: aborting due to 4 previous errors
51+
52+
For more information about this error, try `rustc --explain E0080`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: any use of this value will cause an error
2+
--> $DIR/ub-incorrect-vtable.rs:19:14
3+
|
4+
LL | / const INVALID_VTABLE_ALIGNMENT: &dyn Trait =
5+
LL | | unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) };
6+
| |______________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^__-
7+
| |
8+
| invalid vtable: alignment `1000` is not a power of 2
9+
|
10+
= note: `#[deny(const_err)]` on by default
11+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
12+
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
13+
14+
error: any use of this value will cause an error
15+
--> $DIR/ub-incorrect-vtable.rs:25:14
16+
|
17+
LL | / const INVALID_VTABLE_SIZE: &dyn Trait =
18+
LL | | unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) };
19+
| |______________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^__-
20+
| |
21+
| invalid vtable: size is bigger than largest supported object
22+
|
23+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
24+
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
25+
26+
error[E0080]: it is undefined behavior to use this value
27+
--> $DIR/ub-incorrect-vtable.rs:36:1
28+
|
29+
LL | / const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
30+
LL | | unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), 1usize, 1000usize))) };
31+
| |_____________________________________________________________________________________________^ type validation failed: encountered invalid vtable: alignment `1000` is not a power of 2 at .0
32+
|
33+
= 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.
34+
= note: the raw bytes of the constant (size: 16, align: 8) {
35+
╾───────allocN───────╼ ╾───────allocN───────╼ │ ╾──────╼╾──────╼
36+
}
37+
38+
error[E0080]: it is undefined behavior to use this value
39+
--> $DIR/ub-incorrect-vtable.rs:41:1
40+
|
41+
LL | / const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
42+
LL | | unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), usize::MAX, 1usize))) };
43+
| |______________________________________________________________________________________________^ type validation failed: encountered invalid vtable: size is bigger than largest supported object at .0
44+
|
45+
= 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.
46+
= note: the raw bytes of the constant (size: 16, align: 8) {
47+
╾───────allocN───────╼ ╾───────allocN───────╼ │ ╾──────╼╾──────╼
48+
}
49+
50+
error: aborting due to 4 previous errors
51+
52+
For more information about this error, try `rustc --explain E0080`.

src/test/ui/consts/const-eval/ub-incorrect-vtable.rs

+25
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@
33
// triggers an error
44
// - a similar test that triggers a previously-untested const UB error: emitted close to the above
55
// error, it checks the correctness of the size
6+
//
7+
// As is, this code will only hard error when the constants are used, and the errors are emitted via
8+
// the `#[allow]`-able `const_err` lint. However, if the transparent wrapper technique to prevent
9+
// reborrows is used -- from `ub-wide-ptr.rs` -- these two errors reach validation and would trigger
10+
// ICEs as tracked by #86193. So we also use the transparent wrapper to verify proper validation
11+
// errors are emitted instead of ICEs.
12+
13+
// stderr-per-bitwidth
14+
// normalize-stderr-test "alloc\d+" -> "allocN"
615

716
trait Trait {}
817

@@ -18,4 +27,20 @@ const INVALID_VTABLE_SIZE: &dyn Trait =
1827
//~| WARNING this was previously accepted by the compiler
1928
//~| invalid vtable: size is bigger than largest supported object
2029

30+
#[repr(transparent)]
31+
struct W<T>(T);
32+
33+
// The drop fn is checked before size/align are, so get ourselves a "sufficiently valid" drop fn
34+
fn drop_me(_: *mut usize) {}
35+
36+
const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
37+
unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), 1usize, 1000usize))) };
38+
//~^^ ERROR it is undefined behavior to use this value
39+
//~| invalid vtable: alignment `1000` is not a power of 2
40+
41+
const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
42+
unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), usize::MAX, 1usize))) };
43+
//~^^ ERROR it is undefined behavior to use this value
44+
//~| invalid vtable: size is bigger than largest supported object
45+
2146
fn main() {}

src/test/ui/consts/const-eval/ub-incorrect-vtable.stderr

-27
This file was deleted.

0 commit comments

Comments
 (0)