about summary refs log tree commit diff
path: root/compiler/rustc_mir_transform/src/check_pointers.rs
diff options
context:
space:
mode:
authorBastian Kersting <bkersting@google.com>2025-03-03 13:28:41 +0000
committerBastian Kersting <bkersting@google.com>2025-04-25 12:16:40 +0000
commita3e7c699bc430e29e3035d36e4b3f9682c86c56e (patch)
tree2dd704fca4e490f802134c00fcdcae8022c5bc7a /compiler/rustc_mir_transform/src/check_pointers.rs
parent862156d6f25ccb0d915d2a0c8ebab520d05e4f72 (diff)
downloadrust-a3e7c699bc430e29e3035d36e4b3f9682c86c56e.tar.gz
rust-a3e7c699bc430e29e3035d36e4b3f9682c86c56e.zip
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.
Diffstat (limited to 'compiler/rustc_mir_transform/src/check_pointers.rs')
-rw-r--r--compiler/rustc_mir_transform/src/check_pointers.rs57
1 files changed, 36 insertions, 21 deletions
diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs
index 2d04b621935..7e2d0e4b894 100644
--- a/compiler/rustc_mir_transform/src/check_pointers.rs
+++ b/compiler/rustc_mir_transform/src/check_pointers.rs
@@ -12,13 +12,13 @@ pub(crate) struct PointerCheck<'tcx> {
     pub(crate) assert_kind: Box<AssertKind<Operand<'tcx>>>,
 }
 
-/// Indicates whether we insert the checks for borrow places of a raw pointer.
-/// Concretely places with [MutatingUseContext::Borrow] or
-/// [NonMutatingUseContext::SharedBorrow].
+/// When checking for borrows of field projections (`&(*ptr).a`), we might want
+/// to check for the field type (type of `.a` in the example). This enum defines
+/// the variations (pass the pointer [Ty] or the field [Ty]).
 #[derive(Copy, Clone)]
-pub(crate) enum BorrowCheckMode {
-    IncludeBorrows,
-    ExcludeBorrows,
+pub(crate) enum BorrowedFieldProjectionMode {
+    FollowProjections,
+    NoFollowProjections,
 }
 
 /// Utility for adding a check for read/write on every sized, raw pointer.
@@ -27,8 +27,8 @@ pub(crate) enum BorrowCheckMode {
 /// new basic block directly before the pointer access. (Read/write accesses
 /// are determined by the `PlaceContext` of the MIR visitor.) Then calls
 /// `on_finding` to insert the actual logic for a pointer check (e.g. check for
-/// alignment). A check can choose to be inserted for (mutable) borrows of
-/// raw pointers via the `borrow_check_mode` parameter.
+/// alignment). A check can choose to follow borrows of field projections via
+/// the `field_projection_mode` parameter.
 ///
 /// This utility takes care of the right order of blocks, the only thing a
 /// caller must do in `on_finding` is:
@@ -45,7 +45,7 @@ pub(crate) fn check_pointers<'tcx, F>(
     body: &mut Body<'tcx>,
     excluded_pointees: &[Ty<'tcx>],
     on_finding: F,
-    borrow_check_mode: BorrowCheckMode,
+    field_projection_mode: BorrowedFieldProjectionMode,
 ) where
     F: Fn(
         /* tcx: */ TyCtxt<'tcx>,
@@ -82,7 +82,7 @@ pub(crate) fn check_pointers<'tcx, F>(
                 local_decls,
                 typing_env,
                 excluded_pointees,
-                borrow_check_mode,
+                field_projection_mode,
             );
             finder.visit_statement(statement, location);
 
@@ -128,7 +128,7 @@ struct PointerFinder<'a, 'tcx> {
     typing_env: ty::TypingEnv<'tcx>,
     pointers: Vec<(Place<'tcx>, Ty<'tcx>, PlaceContext)>,
     excluded_pointees: &'a [Ty<'tcx>],
-    borrow_check_mode: BorrowCheckMode,
+    field_projection_mode: BorrowedFieldProjectionMode,
 }
 
 impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
@@ -137,7 +137,7 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
         local_decls: &'a mut LocalDecls<'tcx>,
         typing_env: ty::TypingEnv<'tcx>,
         excluded_pointees: &'a [Ty<'tcx>],
-        borrow_check_mode: BorrowCheckMode,
+        field_projection_mode: BorrowedFieldProjectionMode,
     ) -> Self {
         PointerFinder {
             tcx,
@@ -145,7 +145,7 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
             typing_env,
             excluded_pointees,
             pointers: Vec::new(),
-            borrow_check_mode,
+            field_projection_mode,
         }
     }
 
@@ -163,15 +163,14 @@ impl<'a, 'tcx> PointerFinder<'a, 'tcx> {
                 MutatingUseContext::Store
                 | MutatingUseContext::Call
                 | MutatingUseContext::Yield
-                | MutatingUseContext::Drop,
+                | MutatingUseContext::Drop
+                | MutatingUseContext::Borrow,
             ) => true,
             PlaceContext::NonMutatingUse(
-                NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
+                NonMutatingUseContext::Copy
+                | NonMutatingUseContext::Move
+                | NonMutatingUseContext::SharedBorrow,
             ) => true,
-            PlaceContext::MutatingUse(MutatingUseContext::Borrow)
-            | PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
-                matches!(self.borrow_check_mode, BorrowCheckMode::IncludeBorrows)
-            }
             _ => false,
         }
     }
@@ -194,8 +193,24 @@ impl<'a, 'tcx> Visitor<'tcx> for PointerFinder<'a, 'tcx> {
             return;
         }
 
-        let pointee_ty =
-            pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer");
+        // If we see a borrow of a field projection, we want to pass the field Ty to the
+        // check and not the pointee Ty.
+        let pointee_ty = match self.field_projection_mode {
+            BorrowedFieldProjectionMode::FollowProjections
+                if matches!(
+                    context,
+                    PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow)
+                        | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
+                ) =>
+            {
+                if let Some(PlaceElem::Field(_, ty)) = place.projection.last() {
+                    *ty
+                } else {
+                    pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer")
+                }
+            }
+            _ => pointer_ty.builtin_deref(true).expect("no builtin_deref for an raw pointer"),
+        };
         // Ideally we'd support this in the future, but for now we are limited to sized types.
         if !pointee_ty.is_sized(self.tcx, self.typing_env) {
             trace!("Raw pointer, but pointee is not known to be sized: {:?}", pointer_ty);