From 8b3435c10f458b840d9cba31f2f35586e9b31189 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 5 Sep 2023 22:29:51 +0200 Subject: [PATCH 1/2] fix detecting references to packed unsized fields --- .../rustc_const_eval/src/util/alignment.rs | 4 ++- tests/ui/lint/unaligned_references.rs | 13 ++++++++ tests/ui/lint/unaligned_references.stderr | 32 ++++++++++++------- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_const_eval/src/util/alignment.rs b/compiler/rustc_const_eval/src/util/alignment.rs index 2e0643afb3918..bdb08a9828e07 100644 --- a/compiler/rustc_const_eval/src/util/alignment.rs +++ b/compiler/rustc_const_eval/src/util/alignment.rs @@ -22,9 +22,11 @@ where let ty = place.ty(local_decls, tcx).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() => { // 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. debug!( "is_disaligned({:?}) - align = {}, packed = {}; not disaligned", place, diff --git a/tests/ui/lint/unaligned_references.rs b/tests/ui/lint/unaligned_references.rs index 0c9c79c08b251..1f03afb57d08e 100644 --- a/tests/ui/lint/unaligned_references.rs +++ b/tests/ui/lint/unaligned_references.rs @@ -1,3 +1,6 @@ +use std::mem::ManuallyDrop; +use std::fmt::Debug; + #[repr(packed)] pub struct Good { data: u64, @@ -27,6 +30,16 @@ impl Foo for Packed2 { } } +// Test for #115396 +fn packed_dyn() { + #[repr(packed)] + struct Unaligned(ManuallyDrop); + + let ref local = Unaligned(ManuallyDrop::new([3, 5, 8u64])); + let foo: &Unaligned = &*local; + println!("{:?}", &*foo.0); //~ ERROR reference to packed field +} + fn main() { unsafe { let good = Good { data: 0, ptr: &0, data2: [0, 0], aligned: [0; 32] }; diff --git a/tests/ui/lint/unaligned_references.stderr b/tests/ui/lint/unaligned_references.stderr index d3abc37669f59..b2ff09a4e9a89 100644 --- a/tests/ui/lint/unaligned_references.stderr +++ b/tests/ui/lint/unaligned_references.stderr @@ -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; | ^^^^^^^ @@ -9,7 +9,17 @@ 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:47:17 | LL | let _ = &good.ptr; | ^^^^^^^^^ @@ -19,7 +29,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:48:17 | LL | let _ = &good.data; | ^^^^^^^^^^ @@ -29,7 +39,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:50:17 | LL | let _ = &good.data as *const _; | ^^^^^^^^^^ @@ -39,7 +49,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:51:27 | LL | let _: *const _ = &good.data; | ^^^^^^^^^^ @@ -49,7 +59,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:53:17 | LL | let _ = good.data.clone(); | ^^^^^^^^^ @@ -59,7 +69,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:55:17 | LL | let _ = &good.data2[0]; | ^^^^^^^^^^^^^^ @@ -69,7 +79,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:64:17 | LL | let _ = &packed2.x; | ^^^^^^^^^^ @@ -79,7 +89,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:103:20 | LL | let _ref = &m1.1.a; | ^^^^^^^ @@ -89,7 +99,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:106:20 | LL | let _ref = &m2.1.a; | ^^^^^^^ @@ -98,6 +108,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 11 previous errors For more information about this error, try `rustc --explain E0793`. From ad7045e160b77a6cedea61e85dfbfd3497da05fd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 5 Sep 2023 23:06:35 +0200 Subject: [PATCH 2/2] still accept references to u8 slices and str in packed fields --- .../rustc_const_eval/src/util/alignment.rs | 10 ++++- tests/ui/lint/unaligned_references.rs | 12 +++++- tests/ui/lint/unaligned_references.stderr | 40 ++++++++++++++----- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_const_eval/src/util/alignment.rs b/compiler/rustc_const_eval/src/util/alignment.rs index bdb08a9828e07..8642dfccd7843 100644 --- a/compiler/rustc_const_eval/src/util/alignment.rs +++ b/compiler/rustc_const_eval/src/util/alignment.rs @@ -21,12 +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 && layout.is_sized() => { + 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. + // 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, diff --git a/tests/ui/lint/unaligned_references.rs b/tests/ui/lint/unaligned_references.rs index 1f03afb57d08e..3f6dab35475e8 100644 --- a/tests/ui/lint/unaligned_references.rs +++ b/tests/ui/lint/unaligned_references.rs @@ -33,11 +33,21 @@ impl Foo for Packed2 { // Test for #115396 fn packed_dyn() { #[repr(packed)] - struct Unaligned(ManuallyDrop); + struct Unaligned(ManuallyDrop); let ref local = Unaligned(ManuallyDrop::new([3, 5, 8u64])); let foo: &Unaligned = &*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 = &*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() { diff --git a/tests/ui/lint/unaligned_references.stderr b/tests/ui/lint/unaligned_references.stderr index b2ff09a4e9a89..328cafbd9864b 100644 --- a/tests/ui/lint/unaligned_references.stderr +++ b/tests/ui/lint/unaligned_references.stderr @@ -19,7 +19,27 @@ LL | println!("{:?}", &*foo.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:47:17 + --> $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; | ^^^^^^^^^ @@ -29,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:48:17 + --> $DIR/unaligned_references.rs:58:17 | LL | let _ = &good.data; | ^^^^^^^^^^ @@ -39,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:50:17 + --> $DIR/unaligned_references.rs:60:17 | LL | let _ = &good.data as *const _; | ^^^^^^^^^^ @@ -49,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:51:27 + --> $DIR/unaligned_references.rs:61:27 | LL | let _: *const _ = &good.data; | ^^^^^^^^^^ @@ -59,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:53:17 + --> $DIR/unaligned_references.rs:63:17 | LL | let _ = good.data.clone(); | ^^^^^^^^^ @@ -69,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:55:17 + --> $DIR/unaligned_references.rs:65:17 | LL | let _ = &good.data2[0]; | ^^^^^^^^^^^^^^ @@ -79,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:64:17 + --> $DIR/unaligned_references.rs:74:17 | LL | let _ = &packed2.x; | ^^^^^^^^^^ @@ -89,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:103:20 + --> $DIR/unaligned_references.rs:113:20 | LL | let _ref = &m1.1.a; | ^^^^^^^ @@ -99,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:106:20 + --> $DIR/unaligned_references.rs:116:20 | LL | let _ref = &m2.1.a; | ^^^^^^^ @@ -108,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 11 previous errors +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0793`.