Skip to content

Commit a7ce54d

Browse files
committed
Auto merge of rust-lang#137940 - 1c3t3a:alignment-borrows-check, r=<try>
Extend the alignment check to borrows The current alignment check does not include checks for creating misaligned references from raw pointers, which is now added in this patch. When inserting the check we need to be careful with references to field projections (e.g. `&(*ptr).a`), in which case the resulting reference must be aligned according to the field type and not the type of the pointer. r? `@saethlin` cc `@RalfJung,` after our discussion in rust-lang#134424
2 parents ebf0cf7 + d3ef125 commit a7ce54d

File tree

7 files changed

+89
-28
lines changed

7 files changed

+89
-28
lines changed

compiler/rustc_mir_transform/src/check_alignment.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_middle::mir::*;
55
use rustc_middle::ty::{Ty, TyCtxt};
66
use rustc_session::Session;
77

8-
use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers};
8+
use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers};
99

1010
pub(super) struct CheckAlignment;
1111

@@ -22,15 +22,15 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment {
2222
// Skip trivially aligned place types.
2323
let excluded_pointees = [tcx.types.bool, tcx.types.i8, tcx.types.u8];
2424

25-
// We have to exclude borrows here: in `&x.field`, the exact
26-
// requirement is that the final reference must be aligned, but
27-
// `check_pointers` would check that `x` is aligned, which would be wrong.
25+
// When checking the alignment of references to field projections (`&(*ptr).a`),
26+
// we need to make sure that the reference is aligned according to the field type
27+
// and not to the pointer type.
2828
check_pointers(
2929
tcx,
3030
body,
3131
&excluded_pointees,
3232
insert_alignment_check,
33-
BorrowCheckMode::ExcludeBorrows,
33+
BorrowedFieldProjectionMode::FollowProjections,
3434
);
3535
}
3636

compiler/rustc_mir_transform/src/check_null.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_middle::mir::*;
44
use rustc_middle::ty::{Ty, TyCtxt};
55
use rustc_session::Session;
66

7-
use crate::check_pointers::{BorrowCheckMode, PointerCheck, check_pointers};
7+
use crate::check_pointers::{BorrowedFieldProjectionMode, PointerCheck, check_pointers};
88

99
pub(super) struct CheckNull;
1010

@@ -14,7 +14,13 @@ impl<'tcx> crate::MirPass<'tcx> for CheckNull {
1414
}
1515

1616
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
17-
check_pointers(tcx, body, &[], insert_null_check, BorrowCheckMode::IncludeBorrows);
17+
check_pointers(
18+
tcx,
19+
body,
20+
&[],
21+
insert_null_check,
22+
BorrowedFieldProjectionMode::NoFollowProjections,
23+
);
1824
}
1925

2026
fn is_required(&self) -> bool {

compiler/rustc_mir_transform/src/check_pointers.rs

+36-21
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ pub(crate) struct PointerCheck<'tcx> {
1212
pub(crate) assert_kind: Box<AssertKind<Operand<'tcx>>>,
1313
}
1414

15-
/// Indicates whether we insert the checks for borrow places of a raw pointer.
16-
/// Concretely places with [MutatingUseContext::Borrow] or
17-
/// [NonMutatingUseContext::SharedBorrow].
15+
/// When checking for borrows of field projections (`&(*ptr).a`), we might want
16+
/// to check for the field type (type of `.a` in the example). This enum defines
17+
/// the variations (pass the pointer [Ty] or the field [Ty]).
1818
#[derive(Copy, Clone)]
19-
pub(crate) enum BorrowCheckMode {
20-
IncludeBorrows,
21-
ExcludeBorrows,
19+
pub(crate) enum BorrowedFieldProjectionMode {
20+
FollowProjections,
21+
NoFollowProjections,
2222
}
2323

2424
/// Utility for adding a check for read/write on every sized, raw pointer.
@@ -27,8 +27,8 @@ pub(crate) enum BorrowCheckMode {
2727
/// new basic block directly before the pointer access. (Read/write accesses
2828
/// are determined by the `PlaceContext` of the MIR visitor.) Then calls
2929
/// `on_finding` to insert the actual logic for a pointer check (e.g. check for
30-
/// alignment). A check can choose to be inserted for (mutable) borrows of
31-
/// raw pointers via the `borrow_check_mode` parameter.
30+
/// alignment). A check can choose to follow borrows of field projections via
31+
/// the `field_projection_mode` parameter.
3232
///
3333
/// This utility takes care of the right order of blocks, the only thing a
3434
/// caller must do in `on_finding` is:
@@ -45,7 +45,7 @@ pub(crate) fn check_pointers<'tcx, F>(
4545
body: &mut Body<'tcx>,
4646
excluded_pointees: &[Ty<'tcx>],
4747
on_finding: F,
48-
borrow_check_mode: BorrowCheckMode,
48+
field_projection_mode: BorrowedFieldProjectionMode,
4949
) where
5050
F: Fn(
5151
/* tcx: */ TyCtxt<'tcx>,
@@ -82,7 +82,7 @@ pub(crate) fn check_pointers<'tcx, F>(
8282
local_decls,
8383
typing_env,
8484
excluded_pointees,
85-
borrow_check_mode,
85+
field_projection_mode,
8686
);
8787
finder.visit_statement(statement, location);
8888

@@ -128,7 +128,7 @@ struct PointerFinder<'a, 'tcx> {
128128
typing_env: ty::TypingEnv<'tcx>,
129129
pointers: Vec<(Place<'tcx>, Ty<'tcx>, PlaceContext)>,
130130
excluded_pointees: &'a [Ty<'tcx>],
131-
borrow_check_mode: BorrowCheckMode,
131+
field_projection_mode: BorrowedFieldProjectionMode,
132132
}
133133

134134
impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
@@ -137,15 +137,15 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
137137
local_decls: &'a mut LocalDecls<'tcx>,
138138
typing_env: ty::TypingEnv<'tcx>,
139139
excluded_pointees: &'a [Ty<'tcx>],
140-
borrow_check_mode: BorrowCheckMode,
140+
field_projection_mode: BorrowedFieldProjectionMode,
141141
) -> Self {
142142
PointerFinder {
143143
tcx,
144144
local_decls,
145145
typing_env,
146146
excluded_pointees,
147147
pointers: Vec::new(),
148-
borrow_check_mode,
148+
field_projection_mode,
149149
}
150150
}
151151

@@ -163,15 +163,14 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
163163
MutatingUseContext::Store
164164
| MutatingUseContext::Call
165165
| MutatingUseContext::Yield
166-
| MutatingUseContext::Drop,
166+
| MutatingUseContext::Drop
167+
| MutatingUseContext::Borrow,
167168
) => true,
168169
PlaceContext::NonMutatingUse(
169-
NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
170+
NonMutatingUseContext::Copy
171+
| NonMutatingUseContext::Move
172+
| NonMutatingUseContext::SharedBorrow,
170173
) => true,
171-
PlaceContext::MutatingUse(MutatingUseContext::Borrow)
172-
| PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
173-
matches!(self.borrow_check_mode, BorrowCheckMode::IncludeBorrows)
174-
}
175174
_ => false,
176175
}
177176
}
@@ -194,8 +193,24 @@ impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
194193
return;
195194
}
196195

197-
let pointee_ty =
198-
pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer");
196+
// If we see a borrow of a field projection, we want to pass the field Ty to the
197+
// check and not the pointee Ty.
198+
let pointee_ty = match self.field_projection_mode {
199+
BorrowedFieldProjectionMode::FollowProjections
200+
if matches!(
201+
context,
202+
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
203+
| PlaceContext::MutatingUse(MutatingUseContext::Borrow)
204+
) =>
205+
{
206+
if let Some(PlaceElem::Field(_, ty)) = place.projection.last() {
207+
*ty
208+
} else {
209+
pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer")
210+
}
211+
}
212+
_ => pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer"),
213+
};
199214
// Ideally we'd support this in the future, but for now we are limited to sized types.
200215
if !pointee_ty.is_sized(self.tcx, self.typing_env) {
201216
trace!("Raw pointer, but pointee is not known to be sized: {:?}", pointer_ty);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//@ run-fail
2+
//@ ignore-i686-pc-windows-msvc: #112480
3+
//@ compile-flags: -C debug-assertions
4+
//@ error-pattern: misaligned pointer dereference: address must be a multiple of 0x4 but is
5+
6+
struct Misalignment {
7+
a: u32,
8+
}
9+
10+
fn main() {
11+
let mut items: [Misalignment; 2] = [Misalignment { a: 0 }, Misalignment { a: 1 }];
12+
unsafe {
13+
let ptr: *const Misalignment = items.as_ptr().byte_add(1);
14+
let _ptr: &u32 = unsafe { &(*ptr).a };
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//@ run-fail
2+
//@ ignore-i686-pc-windows-msvc: #112480
3+
//@ compile-flags: -C debug-assertions
4+
//@ error-pattern: misaligned pointer dereference: address must be a multiple of 0x4 but is
5+
6+
fn main() {
7+
let x = [0u32; 2];
8+
let ptr = x.as_ptr();
9+
unsafe {
10+
let _ptr = &(*(ptr.byte_add(1)));
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//@ run-fail
2+
//@ ignore-i686-pc-windows-msvc: #112480
3+
//@ compile-flags: -C debug-assertions
4+
//@ error-pattern: misaligned pointer dereference: address must be a multiple of 0x4 but is
5+
6+
fn main() {
7+
let mut x = [0u32; 2];
8+
let ptr = x.as_mut_ptr();
9+
unsafe {
10+
let _ptr = &mut (*(ptr.byte_add(1)));
11+
}
12+
}

0 commit comments

Comments
 (0)