Skip to content

fix is_disaligned logic for nested packed structs #100064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions compiler/rustc_const_eval/src/util/alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
40 changes: 40 additions & 0 deletions src/test/ui/issues/issue-99838.rs
Original file line number Diff line number Diff line change
@@ -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;
}
53 changes: 53 additions & 0 deletions src/test/ui/lint/unaligned_references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
58 changes: 57 additions & 1 deletion src/test/ui/lint/unaligned_references.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)