Skip to content

Commit dc20137

Browse files
authored
Rollup merge of rust-lang#102513 - RalfJung:no-more-unaligned-reference, r=cjgillot
make unaligned_reference a hard error The `unaligned_references` lint has been warn-by-default since Rust 1.53 (rust-lang#82525) and deny-by-default with mention in cargo future-incompat reports since Rust 1.62 (rust-lang#95372). Current nightly will become Rust 1.66, so (unless major surprises show up with crater) I think it is time we make this a hard error, and close this old soundness gap in the language. Fixes rust-lang#82523.
2 parents 9be2f35 + a0cc385 commit dc20137

25 files changed

+176
-782
lines changed

compiler/rustc_error_codes/src/error_codes.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// /!\ IMPORTANT /!\
66
//
77
// Error messages' format must follow the RFC 1567 available here:
8-
// https://github.com/rust-lang/rfcs/pull/1567
8+
// https://rust-lang.github.io/rfcs/1567-long-error-codes-explanation-normalization.html
99

1010
register_diagnostics! {
1111
E0001: include_str!("./error_codes/E0001.md"),
@@ -494,6 +494,7 @@ E0786: include_str!("./error_codes/E0786.md"),
494494
E0787: include_str!("./error_codes/E0787.md"),
495495
E0788: include_str!("./error_codes/E0788.md"),
496496
E0790: include_str!("./error_codes/E0790.md"),
497+
E0791: include_str!("./error_codes/E0791.md"),
497498
;
498499
// E0006, // merged with E0005
499500
// E0008, // cannot bind by-move into a pattern guard
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
An unaligned reference to a field of a [packed] struct got created.
2+
3+
Erroneous code example:
4+
5+
```compile_fail,E0791
6+
#[repr(packed)]
7+
pub struct Foo {
8+
field1: u64,
9+
field2: u8,
10+
}
11+
12+
unsafe {
13+
let foo = Foo { field1: 0, field2: 0 };
14+
// Accessing the field directly is fine.
15+
let val = foo.field1;
16+
// A reference to a packed field causes a error.
17+
let val = &foo.field1; // ERROR
18+
// An implicit `&` is added in format strings, causing the same error.
19+
println!("{}", foo.field1); // ERROR
20+
}
21+
```
22+
23+
Creating a reference to an insufficiently aligned packed field is
24+
[undefined behavior] and therefore disallowed. Using an `unsafe` block does not
25+
change anything about this. Instead, the code should do a copy of the data in
26+
the packed field or use raw pointers and unaligned accesses.
27+
28+
```
29+
#[repr(packed)]
30+
pub struct Foo {
31+
field1: u64,
32+
field2: u8,
33+
}
34+
35+
unsafe {
36+
let foo = Foo { field1: 0, field2: 0 };
37+
38+
// Instead of a reference, we can create a raw pointer...
39+
let ptr = std::ptr::addr_of!(foo.field1);
40+
// ... and then (crucially!) access it in an explicitly unaligned way.
41+
let val = unsafe { ptr.read_unaligned() };
42+
// This would *NOT* be correct:
43+
// let val = unsafe { *ptr }; // Undefined Behavior due to unaligned load!
44+
45+
// For formatting, we can create a copy to avoid the direct reference.
46+
let copy = foo.field1;
47+
println!("{}", copy);
48+
// Creating a copy can be written in a single line with curly braces.
49+
// (This is equivalent to the two lines above.)
50+
println!("{}", { foo.field1 });
51+
}
52+
```
53+
54+
### Additional information
55+
56+
Note that this error is specifically about *references* to packed fields.
57+
Direct by-value access of those fields is fine, since then the compiler has
58+
enough information to generate the correct kind of access.
59+
60+
See [issue #82523] for more information.
61+
62+
[packed]: https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers
63+
[undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
64+
[issue #82523]: https://github.com/rust-lang/rust/issues/82523

compiler/rustc_lint/src/lib.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,6 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
367367
store.register_renamed("exceeding_bitshifts", "arithmetic_overflow");
368368
store.register_renamed("redundant_semicolon", "redundant_semicolons");
369369
store.register_renamed("overlapping_patterns", "overlapping_range_endpoints");
370-
store.register_renamed("safe_packed_borrows", "unaligned_references");
371370
store.register_renamed("disjoint_capture_migration", "rust_2021_incompatible_closure_captures");
372371
store.register_renamed("or_patterns_back_compat", "rust_2021_incompatible_or_patterns");
373372
store.register_renamed("non_fmt_panic", "non_fmt_panics");
@@ -530,6 +529,16 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
530529
"converted into hard error, see issue #71800 \
531530
<https://github.com/rust-lang/rust/issues/71800> for more information",
532531
);
532+
store.register_removed(
533+
"safe_packed_borrows",
534+
"converted into hard error, see issue #82523 \
535+
<https://github.com/rust-lang/rust/issues/82523> for more information",
536+
);
537+
store.register_removed(
538+
"unaligned_references",
539+
"converted into hard error, see issue #82523 \
540+
<https://github.com/rust-lang/rust/issues/82523> for more information",
541+
);
533542
}
534543

535544
fn register_internals(store: &mut LintStore) {

compiler/rustc_lint_defs/src/builtin.rs

-46
Original file line numberDiff line numberDiff line change
@@ -1146,51 +1146,6 @@ declare_lint! {
11461146
"lints that have been renamed or removed"
11471147
}
11481148

1149-
declare_lint! {
1150-
/// The `unaligned_references` lint detects unaligned references to fields
1151-
/// of [packed] structs.
1152-
///
1153-
/// [packed]: https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers
1154-
///
1155-
/// ### Example
1156-
///
1157-
/// ```compile_fail
1158-
/// #[repr(packed)]
1159-
/// pub struct Foo {
1160-
/// field1: u64,
1161-
/// field2: u8,
1162-
/// }
1163-
///
1164-
/// fn main() {
1165-
/// unsafe {
1166-
/// let foo = Foo { field1: 0, field2: 0 };
1167-
/// let _ = &foo.field1;
1168-
/// println!("{}", foo.field1); // An implicit `&` is added here, triggering the lint.
1169-
/// }
1170-
/// }
1171-
/// ```
1172-
///
1173-
/// {{produces}}
1174-
///
1175-
/// ### Explanation
1176-
///
1177-
/// Creating a reference to an insufficiently aligned packed field is [undefined behavior] and
1178-
/// should be disallowed. Using an `unsafe` block does not change anything about this. Instead,
1179-
/// the code should do a copy of the data in the packed field or use raw pointers and unaligned
1180-
/// accesses. See [issue #82523] for more information.
1181-
///
1182-
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
1183-
/// [issue #82523]: https://github.com/rust-lang/rust/issues/82523
1184-
pub UNALIGNED_REFERENCES,
1185-
Deny,
1186-
"detects unaligned references to fields of packed structs",
1187-
@future_incompatible = FutureIncompatibleInfo {
1188-
reference: "issue #82523 <https://github.com/rust-lang/rust/issues/82523>",
1189-
reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
1190-
};
1191-
report_in_external_macro
1192-
}
1193-
11941149
declare_lint! {
11951150
/// The `const_item_mutation` lint detects attempts to mutate a `const`
11961151
/// item.
@@ -3266,7 +3221,6 @@ declare_lint_pass! {
32663221
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
32673222
INVALID_TYPE_PARAM_DEFAULT,
32683223
RENAMED_AND_REMOVED_LINTS,
3269-
UNALIGNED_REFERENCES,
32703224
CONST_ITEM_MUTATION,
32713225
PATTERNS_IN_FNS_WITHOUT_BODY,
32723226
MISSING_FRAGMENT_SPECIFIER,

compiler/rustc_mir_transform/src/check_packed_ref.rs

+18-39
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
use rustc_errors::struct_span_err;
12
use rustc_hir::def_id::LocalDefId;
23
use rustc_middle::mir::visit::{PlaceContext, Visitor};
34
use rustc_middle::mir::*;
45
use rustc_middle::ty::query::Providers;
56
use rustc_middle::ty::{self, TyCtxt};
6-
use rustc_session::lint::builtin::UNALIGNED_REFERENCES;
77

88
use crate::util;
99
use crate::MirLint;
@@ -31,11 +31,6 @@ struct PackedRefChecker<'a, 'tcx> {
3131
}
3232

3333
fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
34-
let lint_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
35-
36-
// FIXME: when we make this a hard error, this should have its
37-
// own error code.
38-
3934
let extra = if tcx.generics_of(def_id).own_requires_monomorphization() {
4035
"with type or const parameters"
4136
} else {
@@ -46,14 +41,7 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
4641
tcx.item_name(tcx.trait_id_of_impl(def_id.to_def_id()).expect("derived trait name")),
4742
extra
4843
);
49-
50-
tcx.struct_span_lint_hir(
51-
UNALIGNED_REFERENCES,
52-
lint_hir_id,
53-
tcx.def_span(def_id),
54-
message,
55-
|lint| lint,
56-
);
44+
struct_span_err!(tcx.sess, tcx.def_span(def_id), E0791, "{message}").emit();
5745
}
5846

5947
impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
@@ -82,31 +70,22 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
8270
// the impl containing that method should also be.
8371
self.tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local());
8472
} else {
85-
let source_info = self.source_info;
86-
let lint_root = self.body.source_scopes[source_info.scope]
87-
.local_data
88-
.as_ref()
89-
.assert_crate_local()
90-
.lint_root;
91-
self.tcx.struct_span_lint_hir(
92-
UNALIGNED_REFERENCES,
93-
lint_root,
94-
source_info.span,
95-
"reference to packed field is unaligned",
96-
|lint| {
97-
lint
98-
.note(
99-
"fields of packed structs are not properly aligned, and creating \
100-
a misaligned reference is undefined behavior (even if that \
101-
reference is never dereferenced)",
102-
)
103-
.help(
104-
"copy the field contents to a local variable, or replace the \
105-
reference with a raw pointer and use `read_unaligned`/`write_unaligned` \
106-
(loads and stores via `*p` must be properly aligned even when using raw pointers)"
107-
)
108-
},
109-
);
73+
struct_span_err!(
74+
self.tcx.sess,
75+
self.source_info.span,
76+
E0791,
77+
"reference to packed field is unaligned"
78+
)
79+
.note(
80+
"fields of packed structs are not properly aligned, and creating \
81+
a misaligned reference is undefined behavior (even if that \
82+
reference is never dereferenced)",
83+
).help(
84+
"copy the field contents to a local variable, or replace the \
85+
reference with a raw pointer and use `read_unaligned`/`write_unaligned` \
86+
(loads and stores via `*p` must be properly aligned even when using raw pointers)"
87+
)
88+
.emit();
11089
}
11190
}
11291
}

src/test/ui/binding/issue-53114-safety-checks.rs

-4
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,11 @@ fn let_wild_gets_unsafe_field() {
2121
let u2 = U { a: I(1) };
2222
let p = P { a: &2, b: &3 };
2323
let _ = &p.b; //~ ERROR reference to packed field
24-
//~^ WARN will become a hard error
2524
let _ = u1.a; // #53114: should eventually signal error as well
2625
let _ = &u2.a; //~ ERROR [E0133]
2726

2827
// variation on above with `_` in substructure
2928
let (_,) = (&p.b,); //~ ERROR reference to packed field
30-
//~^ WARN will become a hard error
3129
let (_,) = (u1.a,); //~ ERROR [E0133]
3230
let (_,) = (&u2.a,); //~ ERROR [E0133]
3331
}
@@ -37,13 +35,11 @@ fn match_unsafe_field_to_wild() {
3735
let u2 = U { a: I(1) };
3836
let p = P { a: &2, b: &3 };
3937
match &p.b { _ => { } } //~ ERROR reference to packed field
40-
//~^ WARN will become a hard error
4138
match u1.a { _ => { } } //~ ERROR [E0133]
4239
match &u2.a { _ => { } } //~ ERROR [E0133]
4340

4441
// variation on above with `_` in substructure
4542
match (&p.b,) { (_,) => { } } //~ ERROR reference to packed field
46-
//~^ WARN will become a hard error
4743
match (u1.a,) { (_,) => { } } //~ ERROR [E0133]
4844
match (&u2.a,) { (_,) => { } } //~ ERROR [E0133]
4945
}

0 commit comments

Comments
 (0)