Skip to content

Commit bdb72ed

Browse files
arielb1Ariel Ben-Yehuda
authored and
Ariel Ben-Yehuda
committed
make accesses to fields of packed structs unsafe
To handle packed structs with destructors (which you'll think are a rare case, but the `#[repr(packed)] struct Packed<T>(T);` pattern is ever-popular, which requires handling packed structs with destructors to avoid monomorphization-time errors), drops of subfields of packed structs should drop a local move of the field instead of the original one. cc #27060 - this should deal with that issue after codegen of drop glue is updated. The new errors need to be changed to future-compatibility warnings, but I'll rather do a crater run first with them as errors to assess the impact.
1 parent c48650e commit bdb72ed

File tree

7 files changed

+151
-28
lines changed

7 files changed

+151
-28
lines changed

src/librustc_mir/transform/check_unsafety.rs

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxHashSet;
1212
use rustc_data_structures::indexed_vec::IndexVec;
1313

1414
use rustc::ty::maps::Providers;
15-
use rustc::ty::{self, TyCtxt};
15+
use rustc::ty::{self, Ty, TyCtxt};
1616
use rustc::hir;
1717
use rustc::hir::def_id::DefId;
1818
use rustc::lint::builtin::{SAFE_EXTERN_STATICS, UNUSED_UNSAFE};
@@ -29,6 +29,9 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> {
2929
visibility_scope_info: &'a IndexVec<VisibilityScope, VisibilityScopeInfo>,
3030
violations: Vec<UnsafetyViolation>,
3131
source_info: SourceInfo,
32+
// true if an a part of this *memory block* of this expression
33+
// is being borrowed, used for repr(packed) checking.
34+
need_check_packed: bool,
3235
tcx: TyCtxt<'a, 'tcx, 'tcx>,
3336
param_env: ty::ParamEnv<'tcx>,
3437
used_unsafe: FxHashSet<ast::NodeId>,
@@ -50,6 +53,7 @@ impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> {
5053
},
5154
tcx,
5255
param_env,
56+
need_check_packed: false,
5357
used_unsafe: FxHashSet(),
5458
inherited_blocks: vec![],
5559
}
@@ -138,6 +142,14 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
138142
lvalue: &Lvalue<'tcx>,
139143
context: LvalueContext<'tcx>,
140144
location: Location) {
145+
let old_need_check_packed = self.need_check_packed;
146+
if let LvalueContext::Borrow { .. } = context {
147+
let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
148+
if !self.has_align_1(ty) {
149+
self.need_check_packed = true;
150+
}
151+
}
152+
141153
match lvalue {
142154
&Lvalue::Projection(box Projection {
143155
ref base, ref elem
@@ -151,31 +163,39 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
151163
self.source_info = self.mir.local_decls[local].source_info;
152164
}
153165
}
166+
if let &ProjectionElem::Deref = elem {
167+
self.need_check_packed = false;
168+
}
154169
let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx);
155170
match base_ty.sty {
156171
ty::TyRawPtr(..) => {
157172
self.require_unsafe("dereference of raw pointer")
158173
}
159-
ty::TyAdt(adt, _) if adt.is_union() => {
160-
if context == LvalueContext::Store ||
161-
context == LvalueContext::Drop
162-
{
163-
let elem_ty = match elem {
164-
&ProjectionElem::Field(_, ty) => ty,
165-
_ => span_bug!(
166-
self.source_info.span,
167-
"non-field projection {:?} from union?",
168-
lvalue)
169-
};
170-
if elem_ty.moves_by_default(self.tcx, self.param_env,
171-
self.source_info.span) {
172-
self.require_unsafe(
173-
"assignment to non-`Copy` union field")
174+
ty::TyAdt(adt, _) => {
175+
if adt.is_union() {
176+
if context == LvalueContext::Store ||
177+
context == LvalueContext::Drop
178+
{
179+
let elem_ty = match elem {
180+
&ProjectionElem::Field(_, ty) => ty,
181+
_ => span_bug!(
182+
self.source_info.span,
183+
"non-field projection {:?} from union?",
184+
lvalue)
185+
};
186+
if elem_ty.moves_by_default(self.tcx, self.param_env,
187+
self.source_info.span) {
188+
self.require_unsafe(
189+
"assignment to non-`Copy` union field")
190+
} else {
191+
// write to non-move union, safe
192+
}
174193
} else {
175-
// write to non-move union, safe
194+
self.require_unsafe("access to union field")
176195
}
177-
} else {
178-
self.require_unsafe("access to union field")
196+
}
197+
if adt.repr.packed() && self.need_check_packed {
198+
self.require_unsafe("borrow of packed field")
179199
}
180200
}
181201
_ => {}
@@ -199,12 +219,21 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
199219
}], &[]);
200220
}
201221
}
202-
}
222+
};
203223
self.super_lvalue(lvalue, context, location);
224+
self.need_check_packed = old_need_check_packed;
204225
}
205226
}
206227

207228
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
229+
230+
fn has_align_1(&self, ty: Ty<'tcx>) -> bool {
231+
self.tcx.at(self.source_info.span)
232+
.layout_raw(self.param_env.and(ty))
233+
.map(|layout| layout.align.abi() == 1)
234+
.unwrap_or(false)
235+
}
236+
208237
fn require_unsafe(&mut self,
209238
description: &'static str)
210239
{

src/librustc_typeck/check/wfcheck.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use constrained_type_params::{identify_constrained_type_params, Parameter};
1313

1414
use hir::def_id::DefId;
1515
use rustc::traits::{self, ObligationCauseCode};
16-
use rustc::ty::{self, Ty, TyCtxt};
16+
use rustc::ty::{self, Lift, Ty, TyCtxt};
1717
use rustc::ty::util::ExplicitSelf;
1818
use rustc::util::nodemap::{FxHashSet, FxHashMap};
1919
use rustc::middle::lang_items;
@@ -224,10 +224,31 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
224224
{
225225
self.for_item(item).with_fcx(|fcx, this| {
226226
let variants = lookup_fields(fcx);
227+
let def_id = fcx.tcx.hir.local_def_id(item.id);
228+
let packed = fcx.tcx.adt_def(def_id).repr.packed();
227229

228230
for variant in &variants {
229-
// For DST, all intermediate types must be sized.
230-
let unsized_len = if all_sized || variant.fields.is_empty() { 0 } else { 1 };
231+
// For DST, or when drop needs to copy things around, all
232+
// intermediate types must be sized.
233+
let needs_drop_copy = || {
234+
packed && {
235+
let ty = variant.fields.last().unwrap().ty;
236+
let ty = fcx.tcx.erase_regions(&ty).lift_to_tcx(this.tcx)
237+
.unwrap_or_else(|| {
238+
span_bug!(item.span, "inference variables in {:?}", ty)
239+
});
240+
ty.needs_drop(this.tcx, this.tcx.param_env(def_id))
241+
}
242+
};
243+
let unsized_len = if
244+
all_sized ||
245+
variant.fields.is_empty() ||
246+
needs_drop_copy()
247+
{
248+
0
249+
} else {
250+
1
251+
};
231252
for field in &variant.fields[..variant.fields.len() - unsized_len] {
232253
fcx.register_bound(
233254
field.ty,
@@ -246,7 +267,6 @@ impl<'a, 'gcx> CheckTypeWellFormedVisitor<'a, 'gcx> {
246267
}
247268
}
248269

249-
let def_id = fcx.tcx.hir.local_def_id(item.id);
250270
let predicates = fcx.tcx.predicates_of(def_id).instantiate_identity(fcx.tcx);
251271
let predicates = fcx.normalize_associated_types_in(item.span, &predicates);
252272
this.check_where_clauses(fcx, item.span, &predicates);

src/libsyntax_pos/span_encoding.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,30 @@ use hygiene::SyntaxContext;
1919

2020
use rustc_data_structures::fx::FxHashMap;
2121
use std::cell::RefCell;
22+
use std::hash::{Hash, Hasher};
2223

2324
/// A compressed span.
2425
/// Contains either fields of `SpanData` inline if they are small, or index into span interner.
2526
/// The primary goal of `Span` is to be as small as possible and fit into other structures
2627
/// (that's why it uses `packed` as well). Decoding speed is the second priority.
2728
/// See `SpanData` for the info on span fields in decoded representation.
28-
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
29+
#[derive(Clone, Copy)]
2930
#[repr(packed)]
3031
pub struct Span(u32);
3132

33+
impl PartialEq for Span {
34+
#[inline]
35+
fn eq(&self, other: &Self) -> bool { ({self.0}) == ({other.0}) }
36+
}
37+
38+
impl Eq for Span {}
39+
40+
impl Hash for Span {
41+
fn hash<H: Hasher>(&self, s: &mut H) {
42+
{self.0}.hash(s)
43+
}
44+
}
45+
3246
/// Dummy span, both position and length are zero, syntax context is zero as well.
3347
/// This span is kept inline and encoded with format 0.
3448
pub const DUMMY_SP: Span = Span(0);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[repr(packed)]
12+
pub struct Bad<T: ?Sized> {
13+
data: T, //~ ERROR `T: std::marker::Sized` is not satisfied
14+
}
15+
16+
fn main() {}

src/test/compile-fail/issue-27060.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[repr(packed)]
12+
pub struct Good {
13+
data: &'static u32,
14+
data2: [&'static u32; 2],
15+
aligned: [u8; 32],
16+
}
17+
18+
#[repr(packed)]
19+
pub struct JustArray {
20+
array: [u32]
21+
}
22+
23+
fn main() {
24+
let good = Good {
25+
data: &0,
26+
data2: [&0, &0],
27+
aligned: [0; 32]
28+
};
29+
30+
unsafe {
31+
let _ = &good.data; // ok
32+
let _ = &good.data2[0]; // ok
33+
}
34+
35+
let _ = &good.data; //~ ERROR borrow of packed field requires unsafe
36+
let _ = &good.data2[0]; //~ ERROR borrow of packed field requires unsafe
37+
let _ = &*good.data; // ok, behind a pointer
38+
let _ = &good.aligned; // ok, has align 1
39+
let _ = &good.aligned[2]; // ok, has align 1
40+
}

src/test/run-pass/packed-struct-borrow-element.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct Foo {
1717

1818
pub fn main() {
1919
let foo = Foo { bar: 1, baz: 2 };
20-
let brw = &foo.baz;
20+
let brw = unsafe { &foo.baz };
2121

2222
assert_eq!(*brw, 2);
2323
}

src/test/run-pass/packed-struct-optimized-enum.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
// except according to those terms.
1010

1111
#[repr(packed)]
12-
#[derive(Copy, Clone)]
13-
struct Packed<T>(T);
12+
struct Packed<T: Copy>(T);
13+
14+
impl<T: Copy> Copy for Packed<T> {}
15+
impl<T: Copy> Clone for Packed<T> {
16+
fn clone(&self) -> Self { *self }
17+
}
1418

1519
fn main() {
1620
let one = (Some(Packed((&(), 0))), true);

0 commit comments

Comments
 (0)