about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-10-07 10:57:18 +0000
committerbors <bors@rust-lang.org>2023-10-07 10:57:18 +0000
commit48e24629e946339943b194163a81374cf533ce5a (patch)
treece5899ea97136ca585fc19b9ecbb2ff081ca4d0a
parentd087c6fae215484bf47ba6dda1bc836ef4c07ac5 (diff)
parentad7045e160b77a6cedea61e85dfbfd3497da05fd (diff)
downloadrust-48e24629e946339943b194163a81374cf533ce5a.tar.gz
rust-48e24629e946339943b194163a81374cf533ce5a.zip
Auto merge of #115583 - RalfJung:packed-unsized, r=lcnr
fix detecting references to packed unsized fields

Fixes https://github.com/rust-lang/rust/issues/115396

This is a breaking change, but permitted as a soundness fix.
-rw-r--r--compiler/rustc_const_eval/src/util/alignment.rs10
-rw-r--r--tests/ui/lint/unaligned_references.rs23
-rw-r--r--tests/ui/lint/unaligned_references.stderr52
3 files changed, 73 insertions, 12 deletions
diff --git a/compiler/rustc_const_eval/src/util/alignment.rs b/compiler/rustc_const_eval/src/util/alignment.rs
index 2e0643afb39..8642dfccd78 100644
--- a/compiler/rustc_const_eval/src/util/alignment.rs
+++ b/compiler/rustc_const_eval/src/util/alignment.rs
@@ -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,
diff --git a/tests/ui/lint/unaligned_references.rs b/tests/ui/lint/unaligned_references.rs
index 0c9c79c08b2..3f6dab35475 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,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] };
diff --git a/tests/ui/lint/unaligned_references.stderr b/tests/ui/lint/unaligned_references.stderr
index d3abc37669f..328cafbd986 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,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;
    |                 ^^^^^^^^^
@@ -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;
    |                 ^^^^^^^^^^
@@ -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 _;
    |                 ^^^^^^^^^^
@@ -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;
    |                           ^^^^^^^^^^
@@ -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();
    |                 ^^^^^^^^^
@@ -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];
    |                 ^^^^^^^^^^^^^^
@@ -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;
    |                 ^^^^^^^^^^
@@ -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;
    |                    ^^^^^^^
@@ -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;
    |                    ^^^^^^^
@@ -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`.