Skip to content

Commit 87625db

Browse files
authored
Rollup merge of #118540 - RalfJung:unsized-packed-offset, r=TaKO8Ki
codegen, miri: fix computing the offset of an unsized field in a packed struct `#[repr(packed)]` strikes again. Fixes #118537 Fixes rust-lang/miri#3200 `@bjorn3` I assume cranelift needs the same fix.
2 parents da30882 + ef15a81 commit 87625db

File tree

6 files changed

+171
-11
lines changed

6 files changed

+171
-11
lines changed

compiler/rustc_codegen_ssa/src/mir/place.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
143143
// Simple cases, which don't need DST adjustment:
144144
// * no metadata available - just log the case
145145
// * known alignment - sized types, `[T]`, `str` or a foreign type
146-
// * packed struct - there is no alignment padding
146+
// Note that looking at `field.align` is incorrect since that is not necessarily equal
147+
// to the dynamic alignment of the type.
147148
match field.ty.kind() {
148149
_ if self.llextra.is_none() => {
149150
debug!(
@@ -154,14 +155,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
154155
}
155156
_ if field.is_sized() => return simple(),
156157
ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(),
157-
ty::Adt(def, _) => {
158-
if def.repr().packed() {
159-
// FIXME(eddyb) generalize the adjustment when we
160-
// start supporting packing to larger alignments.
161-
assert_eq!(self.layout.align.abi.bytes(), 1);
162-
return simple();
163-
}
164-
}
165158
_ => {}
166159
}
167160

@@ -186,7 +179,16 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
186179
let unaligned_offset = bx.cx().const_usize(offset.bytes());
187180

188181
// Get the alignment of the field
189-
let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta);
182+
let (_, mut unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta);
183+
184+
// For packed types, we need to cap alignment.
185+
if let ty::Adt(def, _) = self.layout.ty.kind()
186+
&& let Some(packed) = def.repr().pack
187+
{
188+
let packed = bx.const_usize(packed.bytes());
189+
let cmp = bx.icmp(IntPredicate::IntULT, unsized_align, packed);
190+
unsized_align = bx.select(cmp, unsized_align, packed)
191+
}
190192

191193
// Bump the unaligned offset up to the appropriate alignment
192194
let offset = round_up_const_value_to_alignment(bx, unaligned_offset, unsized_align);

compiler/rustc_const_eval/src/interpret/projection.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,17 @@ where
163163
// With custom DSTS, this *will* execute user-defined code, but the same
164164
// happens at run-time so that's okay.
165165
match self.size_and_align_of(&base_meta, &field_layout)? {
166-
Some((_, align)) => (base_meta, offset.align_to(align)),
166+
Some((_, align)) => {
167+
// For packed types, we need to cap alignment.
168+
let align = if let ty::Adt(def, _) = base.layout().ty.kind()
169+
&& let Some(packed) = def.repr().pack
170+
{
171+
align.min(packed)
172+
} else {
173+
align
174+
};
175+
(base_meta, offset.align_to(align))
176+
}
167177
None => {
168178
// For unsized types with an extern type tail we perform no adjustments.
169179
// NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#![feature(layout_for_ptr)]
2+
use std::mem;
3+
4+
#[repr(packed, C)]
5+
struct PackedSized {
6+
f: u8,
7+
d: [u32; 4],
8+
}
9+
10+
#[repr(packed, C)]
11+
struct PackedUnsized {
12+
f: u8,
13+
d: [u32],
14+
}
15+
16+
impl PackedSized {
17+
fn unsize(&self) -> &PackedUnsized {
18+
// We can't unsize via a generic type since then we get the error
19+
// that packed structs with unsized tail don't work if the tail
20+
// might need dropping.
21+
let len = 4usize;
22+
unsafe { mem::transmute((self, len)) }
23+
}
24+
}
25+
26+
fn main() { unsafe {
27+
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
28+
let p = p.unsize() as *const PackedUnsized;
29+
// Make sure the size computation does *not* think there is
30+
// any padding in front of the `d` field.
31+
assert_eq!(mem::size_of_val_raw(p), 1 + 4*4);
32+
// And likewise for the offset computation.
33+
let d = std::ptr::addr_of!((*p).d);
34+
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
35+
} }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#![feature(layout_for_ptr)]
2+
use std::mem;
3+
4+
#[repr(packed(4))]
5+
struct Slice([u32]);
6+
7+
#[repr(packed(2), C)]
8+
struct PackedSized {
9+
f: u8,
10+
d: [u32; 4],
11+
}
12+
13+
#[repr(packed(2), C)]
14+
struct PackedUnsized {
15+
f: u8,
16+
d: Slice,
17+
}
18+
19+
impl PackedSized {
20+
fn unsize(&self) -> &PackedUnsized {
21+
// We can't unsize via a generic type since then we get the error
22+
// that packed structs with unsized tail don't work if the tail
23+
// might need dropping.
24+
let len = 4usize;
25+
unsafe { mem::transmute((self, len)) }
26+
}
27+
}
28+
29+
fn main() { unsafe {
30+
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
31+
let p = p.unsize() as *const PackedUnsized;
32+
// Make sure the size computation correctly adds exact 1 byte of padding
33+
// in front of the `d` field.
34+
assert_eq!(mem::size_of_val_raw(p), 1 + 1 + 4*4);
35+
// And likewise for the offset computation.
36+
let d = std::ptr::addr_of!((*p).d);
37+
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
38+
} }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// run-pass
2+
#![feature(layout_for_ptr)]
3+
use std::mem;
4+
5+
#[repr(packed(4))]
6+
struct Slice([u32]);
7+
8+
#[repr(packed(2), C)]
9+
struct PackedSized {
10+
f: u8,
11+
d: [u32; 4],
12+
}
13+
14+
#[repr(packed(2), C)]
15+
struct PackedUnsized {
16+
f: u8,
17+
d: Slice,
18+
}
19+
20+
impl PackedSized {
21+
fn unsize(&self) -> &PackedUnsized {
22+
// We can't unsize via a generic type since then we get the error
23+
// that packed structs with unsized tail don't work if the tail
24+
// might need dropping.
25+
let len = 4usize;
26+
unsafe { mem::transmute((self, len)) }
27+
}
28+
}
29+
30+
fn main() { unsafe {
31+
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
32+
let p = p.unsize() as *const PackedUnsized;
33+
// Make sure the size computation correctly adds exact 1 byte of padding
34+
// in front of the `d` field.
35+
assert_eq!(mem::size_of_val_raw(p), 1 + 1 + 4*4);
36+
// And likewise for the offset computation.
37+
let d = std::ptr::addr_of!((*p).d);
38+
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
39+
} }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// run-pass
2+
#![feature(layout_for_ptr)]
3+
use std::mem;
4+
5+
#[repr(packed, C)]
6+
struct PackedSized {
7+
f: u8,
8+
d: [u32; 4],
9+
}
10+
11+
#[repr(packed, C)]
12+
struct PackedUnsized {
13+
f: u8,
14+
d: [u32],
15+
}
16+
17+
impl PackedSized {
18+
fn unsize(&self) -> &PackedUnsized {
19+
// We can't unsize via a generic type since then we get the error
20+
// that packed structs with unsized tail don't work if the tail
21+
// might need dropping.
22+
let len = 4usize;
23+
unsafe { mem::transmute((self, len)) }
24+
}
25+
}
26+
27+
fn main() { unsafe {
28+
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
29+
let p = p.unsize() as *const PackedUnsized;
30+
// Make sure the size computation does *not* think there is
31+
// any padding in front of the `d` field.
32+
assert_eq!(mem::size_of_val_raw(p), 1 + 4*4);
33+
// And likewise for the offset computation.
34+
let d = std::ptr::addr_of!((*p).d);
35+
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
36+
} }

0 commit comments

Comments
 (0)