Auto merge of #100064 - RalfJung:disaligned, r=petrochenkov

fix is_disaligned logic for nested packed structs

https://github.com/rust-lang/rust/pull/83605 broke the `is_disaligned` logic by bailing out of the loop in `is_within_packed` early. This PR fixes that problem and adds suitable tests.

Fixes https://github.com/rust-lang/rust/issues/99838
This commit is contained in:
bors 2022-08-03 16:09:56 +00:00
commit d6b96b61e7
4 changed files with 162 additions and 17 deletions

View File

@ -48,20 +48,16 @@ fn is_within_packed<'tcx, L>(
where
L: HasLocalDecls<'tcx>,
{
for (place_base, elem) in place.iter_projections().rev() {
match elem {
// encountered a Deref, which is ABI-aligned
ProjectionElem::Deref => break,
ProjectionElem::Field(..) => {
let ty = place_base.ty(local_decls, tcx).ty;
match ty.kind() {
ty::Adt(def, _) => return def.repr().pack,
_ => {}
}
}
_ => {}
}
}
None
place
.iter_projections()
.rev()
// Stop at `Deref`; standard ABI alignment applies there.
.take_while(|(_base, elem)| !matches!(elem, ProjectionElem::Deref))
// Consider the packed alignments at play here...
.filter_map(|(base, _elem)| {
base.ty(local_decls, tcx).ty.ty_adt_def().and_then(|adt| adt.repr().pack)
})
// ... and compute their minimum.
// The overall smallest alignment is what matters.
.min()
}

View File

@ -0,0 +1,40 @@
// run-pass
#![feature(bench_black_box)]
use std::hint;
struct U16(u16);
impl Drop for U16 {
fn drop(&mut self) {
// Prevent LLVM from optimizing away our alignment check.
assert!(hint::black_box(self as *mut U16 as usize) % 2 == 0);
}
}
struct HasDrop;
impl Drop for HasDrop {
fn drop(&mut self) {}
}
struct Wrapper {
_a: U16,
b: HasDrop,
}
#[repr(packed)]
struct Misalign(u8, Wrapper);
fn main() {
let m = Misalign(
0,
Wrapper {
_a: U16(10),
b: HasDrop,
},
);
// Put it somewhere definitely even (so the `a` field is definitely at an odd address).
let m: ([u16; 0], Misalign) = ([], m);
// Move out one field, so we run custom per-field drop logic below.
let _x = m.1.1.b;
}

View File

@ -47,4 +47,57 @@ fn main() {
let _ = &packed2.y; // ok, has align 2 in packed(2) struct
let _ = &packed2.z; // ok, has align 1
}
unsafe {
struct U16(u16);
impl Drop for U16 {
fn drop(&mut self) {
println!("{:p}", self);
}
}
struct HasDrop;
impl Drop for HasDrop {
fn drop(&mut self) {}
}
#[allow(unused)]
struct Wrapper {
a: U16,
b: HasDrop,
}
#[allow(unused)]
#[repr(packed(2))]
struct Wrapper2 {
a: U16,
b: HasDrop,
}
// An outer struct with more restrictive packing than the inner struct -- make sure we
// notice that!
#[repr(packed)]
struct Misalign<T>(u8, T);
let m1 = Misalign(
0,
Wrapper {
a: U16(10),
b: HasDrop,
},
);
let _ref = &m1.1.a; //~ ERROR reference to packed field
//~^ previously accepted
let m2 = Misalign(
0,
Wrapper2 {
a: U16(10),
b: HasDrop,
},
);
let _ref = &m2.1.a; //~ ERROR reference to packed field
//~^ previously accepted
}
}

View File

@ -80,7 +80,29 @@ LL | let _ = &packed2.x;
= note: fields of packed structs are not properly aligned, and 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 7 previous errors
error: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:90:20
|
LL | let _ref = &m1.1.a;
| ^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523>
= note: fields of packed structs are not properly aligned, and 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: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:100:20
|
LL | let _ref = &m2.1.a;
| ^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523>
= note: fields of packed structs are not properly aligned, and 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 9 previous errors
Future incompatibility report: Future breakage diagnostic:
error: reference to packed field is unaligned
@ -201,3 +223,37 @@ LL | #![deny(unaligned_references)]
= note: fields of packed structs are not properly aligned, and 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)
Future breakage diagnostic:
error: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:90:20
|
LL | let _ref = &m1.1.a;
| ^^^^^^^
|
note: the lint level is defined here
--> $DIR/unaligned_references.rs:1:9
|
LL | #![deny(unaligned_references)]
| ^^^^^^^^^^^^^^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523>
= note: fields of packed structs are not properly aligned, and 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)
Future breakage diagnostic:
error: reference to packed field is unaligned
--> $DIR/unaligned_references.rs:100:20
|
LL | let _ref = &m2.1.a;
| ^^^^^^^
|
note: the lint level is defined here
--> $DIR/unaligned_references.rs:1:9
|
LL | #![deny(unaligned_references)]
| ^^^^^^^^^^^^^^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523>
= note: fields of packed structs are not properly aligned, and 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)