Skip to content

Commit 899bc14

Browse files
committed
fix validating fat pointers to user-defined unsized types
1 parent 14dc780 commit 899bc14

File tree

4 files changed

+56
-67
lines changed

4 files changed

+56
-67
lines changed

src/librustc_mir/interpret/place.rs

+2
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
774774
let (size, align) = self.read_size_and_align_from_vtable(vtable)?;
775775
assert_eq!(size, layout.size);
776776
assert_eq!(align.abi(), layout.align.abi()); // only ABI alignment is preserved
777+
// FIXME: More checks for the vtable? We could make sure it is exactly
778+
// the one one would expect for this type.
777779
// Done!
778780
layout
779781
},

src/librustc_mir/interpret/validity.rs

+26-53
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc::mir::interpret::{
99
};
1010

1111
use super::{
12-
MPlaceTy, PlaceExtra, Machine, EvalContext
12+
MPlaceTy, Machine, EvalContext
1313
};
1414

1515
macro_rules! validation_failure{
@@ -281,59 +281,32 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
281281
},
282282
layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
283283
// Fat pointers need special treatment.
284-
match dest.layout.ty.builtin_deref(true).map(|tam| &tam.ty.sty) {
285-
| Some(ty::TyStr)
286-
| Some(ty::TySlice(_)) => {
287-
// check the length (for nicer error messages); must be valid even
288-
// for a raw pointer.
289-
let len_mplace = self.mplace_field(dest, 1)?;
290-
let len = self.read_scalar(len_mplace.into())?;
291-
let len = match len.to_bits(len_mplace.layout.size) {
292-
Err(_) => return validation_failure!("length is not a valid integer", path),
293-
Ok(len) => len as u64,
294-
};
295-
// for safe ptrs, get the fat ptr, and recursively check it
296-
if !dest.layout.ty.is_unsafe_ptr() {
297-
let ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
298-
assert_eq!(ptr.extra, PlaceExtra::Length(len));
299-
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
300-
if seen.insert(unpacked_ptr) {
301-
todo.push((unpacked_ptr, path_clone_and_deref(path)));
302-
}
303-
}
304-
},
305-
Some(ty::TyDynamic(..)) => {
306-
// check the vtable (for nicer error messages); must be valid even for a
307-
// raw ptr.
308-
let vtable = self.read_scalar(self.mplace_field(dest, 1)?.into())?;
309-
let vtable = match vtable.to_ptr() {
310-
Err(_) => return validation_failure!("vtable address is not a pointer", path),
311-
Ok(vtable) => vtable,
312-
};
313-
// for safe ptrs, get the fat ptr, and recursively check it
314-
if !dest.layout.ty.is_unsafe_ptr() {
315-
let ptr = self.ref_to_mplace(self.read_value(dest.into())?)?;
316-
assert_eq!(ptr.extra, PlaceExtra::Vtable(vtable));
317-
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
318-
if seen.insert(unpacked_ptr) {
319-
todo.push((unpacked_ptr, path_clone_and_deref(path)));
320-
}
321-
// FIXME: More checks for the vtable... making sure it is exactly
322-
// the one one would expect for this type.
323-
}
324-
},
325-
Some(ty) =>
326-
bug!("Unexpected fat pointer target type {:?}", ty),
327-
None => {
328-
// Not a pointer, perform regular aggregate handling below
329-
for i in 0..offsets.len() {
330-
let field = self.mplace_field(dest, i as u64)?;
331-
path.push(self.aggregate_field_path_elem(dest.layout.ty, variant, i));
332-
self.validate_mplace(field, path, seen, todo)?;
333-
path.truncate(path_len);
284+
if dest.layout.ty.builtin_deref(true).is_some() {
285+
// This is a fat pointer.
286+
let ptr = match self.ref_to_mplace(self.read_value(dest.into())?) {
287+
Ok(ptr) => ptr,
288+
Err(ReadPointerAsBytes) =>
289+
return validation_failure!("fat pointer length is not a valid integer", path),
290+
Err(ReadBytesAsPointer) =>
291+
return validation_failure!("fat pointer vtable is not a valid pointer", path),
292+
Err(err) => return Err(err),
293+
};
294+
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?;
295+
// for safe ptrs, recursively check it
296+
if !dest.layout.ty.is_unsafe_ptr() {
297+
if seen.insert(unpacked_ptr) {
298+
todo.push((unpacked_ptr, path_clone_and_deref(path)));
334299
}
335-
// FIXME: For a TyStr, check that this is valid UTF-8.
336-
},
300+
}
301+
} else {
302+
// Not a pointer, perform regular aggregate handling below
303+
for i in 0..offsets.len() {
304+
let field = self.mplace_field(dest, i as u64)?;
305+
path.push(self.aggregate_field_path_elem(dest.layout.ty, variant, i));
306+
self.validate_mplace(field, path, seen, todo)?;
307+
path.truncate(path_len);
308+
}
309+
// FIXME: For a TyStr, check that this is valid UTF-8.
337310
}
338311
}
339312
}

src/test/ui/union-ub-fat-ptr.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ union SliceTransmute {
3737
bad: BadSliceRepr,
3838
slice: &'static [u8],
3939
str: &'static str,
40+
my_str: &'static Str,
4041
}
4142

4243
#[repr(C)]
@@ -70,6 +71,8 @@ union DynTransmute {
7071
trait Trait {}
7172
impl Trait for bool {}
7273

74+
struct Str(str);
75+
7376
// OK
7477
const A: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.str};
7578
// bad str
@@ -78,14 +81,17 @@ const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 }
7881
// bad str
7982
const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str};
8083
//~^ ERROR this constant likely exhibits undefined behavior
84+
// bad str in Str
85+
const C2: &Str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str};
86+
//~^ ERROR this constant likely exhibits undefined behavior
8187

8288
// OK
8389
const A2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.slice};
8490
// bad slice
8591
const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice};
8692
//~^ ERROR this constant likely exhibits undefined behavior
8793
// bad slice
88-
const C2: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
94+
const C3: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
8995
//~^ ERROR this constant likely exhibits undefined behavior
9096

9197
// bad trait object

src/test/ui/union-ub-fat-ptr.stderr

+21-13
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,83 @@
11
error[E0080]: this constant likely exhibits undefined behavior
2-
--> $DIR/union-ub-fat-ptr.rs:76:1
2+
--> $DIR/union-ub-fat-ptr.rs:79:1
33
|
44
LL | const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str};
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N
66
|
77
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
88

99
error[E0080]: this constant likely exhibits undefined behavior
10-
--> $DIR/union-ub-fat-ptr.rs:79:1
10+
--> $DIR/union-ub-fat-ptr.rs:82:1
1111
|
1212
LL | const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str};
13-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered length is not a valid integer
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer
1414
|
1515
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
1616

1717
error[E0080]: this constant likely exhibits undefined behavior
1818
--> $DIR/union-ub-fat-ptr.rs:85:1
1919
|
20+
LL | const C2: &Str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str};
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer
22+
|
23+
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
24+
25+
error[E0080]: this constant likely exhibits undefined behavior
26+
--> $DIR/union-ub-fat-ptr.rs:91:1
27+
|
2028
LL | const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice};
2129
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N
2230
|
2331
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
2432

2533
error[E0080]: this constant likely exhibits undefined behavior
26-
--> $DIR/union-ub-fat-ptr.rs:88:1
34+
--> $DIR/union-ub-fat-ptr.rs:94:1
2735
|
28-
LL | const C2: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
29-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered length is not a valid integer
36+
LL | const C3: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer
3038
|
3139
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
3240

3341
error[E0080]: this constant likely exhibits undefined behavior
34-
--> $DIR/union-ub-fat-ptr.rs:92:1
42+
--> $DIR/union-ub-fat-ptr.rs:98:1
3543
|
3644
LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust};
3745
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tried to access memory with alignment N, but alignment N is required
3846
|
3947
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
4048

4149
error[E0080]: this constant likely exhibits undefined behavior
42-
--> $DIR/union-ub-fat-ptr.rs:95:1
50+
--> $DIR/union-ub-fat-ptr.rs:101:1
4351
|
4452
LL | const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust};
4553
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a memory access tried to interpret some bytes as a pointer
4654
|
4755
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
4856

4957
error[E0080]: this constant likely exhibits undefined behavior
50-
--> $DIR/union-ub-fat-ptr.rs:98:1
58+
--> $DIR/union-ub-fat-ptr.rs:104:1
5159
|
5260
LL | const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust};
53-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered vtable address is not a pointer
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer
5462
|
5563
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
5664

5765
error[E0080]: this constant likely exhibits undefined behavior
58-
--> $DIR/union-ub-fat-ptr.rs:102:1
66+
--> $DIR/union-ub-fat-ptr.rs:108:1
5967
|
6068
LL | const G: &Trait = &unsafe { BoolTransmute { val: 3 }.bl };
6169
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>, but expected something in the range 0..=1
6270
|
6371
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
6472

6573
error[E0080]: this constant likely exhibits undefined behavior
66-
--> $DIR/union-ub-fat-ptr.rs:106:1
74+
--> $DIR/union-ub-fat-ptr.rs:112:1
6775
|
6876
LL | const H: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }];
6977
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>[0], but expected something in the range 0..=1
7078
|
7179
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
7280

73-
error: aborting due to 9 previous errors
81+
error: aborting due to 10 previous errors
7482

7583
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)