Skip to content

Commit

Permalink
Auto merge of #115583 - RalfJung:packed-unsized, r=lcnr
Browse files Browse the repository at this point in the history
fix detecting references to packed unsized fields

Fixes #115396

This is a breaking change, but permitted as a soundness fix.
  • Loading branch information
bors committed Oct 7, 2023
2 parents d087c6f + ad7045e commit 48e2462
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 12 deletions.
10 changes: 9 additions & 1 deletion compiler/rustc_const_eval/src/util/alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,18 @@ where
};

let ty = place.ty(local_decls, tcx).ty;
let unsized_tail = || tcx.struct_tail_with_normalize(ty, |ty| ty, || {});
match tcx.layout_of(param_env.and(ty)) {
Ok(layout) if layout.align.abi <= pack => {
Ok(layout)
if layout.align.abi <= pack
&& (layout.is_sized()
|| matches!(unsized_tail().kind(), ty::Slice(..) | ty::Str)) =>
{
// If the packed alignment is greater or equal to the field alignment, the type won't be
// further disaligned.
// However we need to ensure the field is sized; for unsized fields, `layout.align` is
// just an approximation -- except when the unsized tail is a slice, where the alignment
// is fully determined by the type.
debug!(
"is_disaligned({:?}) - align = {}, packed = {}; not disaligned",
place,
Expand Down
23 changes: 23 additions & 0 deletions tests/ui/lint/unaligned_references.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use std::mem::ManuallyDrop;
use std::fmt::Debug;

#[repr(packed)]
pub struct Good {
data: u64,
Expand Down Expand Up @@ -27,6 +30,26 @@ impl Foo for Packed2 {
}
}

// Test for #115396
fn packed_dyn() {
#[repr(packed)]
struct Unaligned<T: ?Sized>(ManuallyDrop<T>);

let ref local = Unaligned(ManuallyDrop::new([3, 5, 8u64]));
let foo: &Unaligned<dyn Debug> = &*local;
println!("{:?}", &*foo.0); //~ ERROR reference to packed field
let foo: &Unaligned<[u64]> = &*local;
println!("{:?}", &*foo.0); //~ ERROR reference to packed field

// Even if the actual alignment is 1, we cannot know that when looking at `dyn Debug.`
let ref local = Unaligned(ManuallyDrop::new([3, 5, 8u8]));
let foo: &Unaligned<dyn Debug> = &*local;
println!("{:?}", &*foo.0); //~ ERROR reference to packed field
// However, we *can* know the alignment when looking at a slice.
let foo: &Unaligned<[u8]> = &*local;
println!("{:?}", &*foo.0); // no error!
}

fn main() {
unsafe {
let good = Good { data: 0, ptr: &0, data2: [0, 0], aligned: [0; 32] };
Expand Down
52 changes: 41 additions & 11 deletions tests/ui/lint/unaligned_references.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:25:13
--> $DIR/unaligned_references.rs:28:13
|
LL | &self.x;
| ^^^^^^^
Expand All @@ -9,7 +9,37 @@ LL | &self.x;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:34:17
--> $DIR/unaligned_references.rs:40:24
|
LL | println!("{:?}", &*foo.0);
| ^^^^^
|
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:42:24
|
LL | println!("{:?}", &*foo.0);
| ^^^^^
|
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:47:24
|
LL | println!("{:?}", &*foo.0);
| ^^^^^
|
= note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:57:17
|
LL | let _ = &good.ptr;
| ^^^^^^^^^
Expand All @@ -19,7 +49,7 @@ LL | let _ = &good.ptr;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:35:17
--> $DIR/unaligned_references.rs:58:17
|
LL | let _ = &good.data;
| ^^^^^^^^^^
Expand All @@ -29,7 +59,7 @@ LL | let _ = &good.data;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:37:17
--> $DIR/unaligned_references.rs:60:17
|
LL | let _ = &good.data as *const _;
| ^^^^^^^^^^
Expand All @@ -39,7 +69,7 @@ LL | let _ = &good.data as *const _;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:38:27
--> $DIR/unaligned_references.rs:61:27
|
LL | let _: *const _ = &good.data;
| ^^^^^^^^^^
Expand All @@ -49,7 +79,7 @@ LL | let _: *const _ = &good.data;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:40:17
--> $DIR/unaligned_references.rs:63:17
|
LL | let _ = good.data.clone();
| ^^^^^^^^^
Expand All @@ -59,7 +89,7 @@ LL | let _ = good.data.clone();
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:42:17
--> $DIR/unaligned_references.rs:65:17
|
LL | let _ = &good.data2[0];
| ^^^^^^^^^^^^^^
Expand All @@ -69,7 +99,7 @@ LL | let _ = &good.data2[0];
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:51:17
--> $DIR/unaligned_references.rs:74:17
|
LL | let _ = &packed2.x;
| ^^^^^^^^^^
Expand All @@ -79,7 +109,7 @@ LL | let _ = &packed2.x;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:90:20
--> $DIR/unaligned_references.rs:113:20
|
LL | let _ref = &m1.1.a;
| ^^^^^^^
Expand All @@ -89,7 +119,7 @@ LL | let _ref = &m1.1.a;
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error[E0793]: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:93:20
--> $DIR/unaligned_references.rs:116:20
|
LL | let _ref = &m2.1.a;
| ^^^^^^^
Expand All @@ -98,6 +128,6 @@ LL | let _ref = &m2.1.a;
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

error: aborting due to 10 previous errors
error: aborting due to 13 previous errors

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

0 comments on commit 48e2462

Please sign in to comment.