11use std:: assert_matches;
22
33use itertools:: Itertools as _;
4- use rustc_abi:: { self as abi, BackendRepr , FIRST_VARIANT } ;
4+ use rustc_abi:: { self as abi, BackendRepr , ExternAbi , FIRST_VARIANT } ;
55use rustc_index:: IndexVec ;
6+ use rustc_middle:: ptrauth:: {
7+ ptrauth_clone_discriminated_schema_for, ptrauth_compute_fn_ptr_type_discriminator_for,
8+ } ;
69use rustc_middle:: ty:: adjustment:: PointerCoercion ;
710use rustc_middle:: ty:: layout:: { HasTyCtxt , HasTypingEnv , LayoutOf , TyAndLayout } ;
811use rustc_middle:: ty:: { self , Instance , Mutability , Ty , TyCtxt } ;
@@ -17,6 +20,13 @@ use crate::common::{IntPredicate, TypeKind};
1720use crate :: traits:: * ;
1821use crate :: { MemFlags , base} ;
1922
23+ /// Type metadata used when applying pointer authentication semantics during
24+ /// transmute lowering.
25+ struct TransmuteInfo < ' tcx > {
26+ src_ty : Ty < ' tcx > ,
27+ dst_ty : Ty < ' tcx > ,
28+ }
29+
2030impl < ' a , ' tcx , Bx : BuilderMethods < ' a , ' tcx > > FunctionCx < ' a , ' tcx , Bx > {
2131 fn try_codegen_const_aggregate_as_immediate (
2232 & mut self ,
@@ -91,6 +101,126 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
91101 true
92102 }
93103
104+ /// Applies pointer-authentication-aware semantic transmute, that is ensuring that when a
105+ /// function pointer is transmuted between two types that map to different type
106+ /// discriminators, the resulting pointer is re-signed appropriately.
107+ ///
108+ /// Only SSA `OperandValue::Immediate` values are eligible for this path.
109+ fn ptrauth_codegen_transmute_operand (
110+ & mut self ,
111+ bx : & mut Bx ,
112+ operand : OperandRef < ' tcx , Bx :: Value > ,
113+ cast : TyAndLayout < ' tcx > ,
114+ ) -> OperandValue < Bx :: Value > {
115+ let val = self . codegen_transmute_operand ( bx, operand, cast) ;
116+
117+ let OperandValue :: Immediate ( ptr) = val else {
118+ return val;
119+ } ;
120+
121+ let info = TransmuteInfo { src_ty : operand. layout . ty , dst_ty : cast. ty } ;
122+
123+ OperandValue :: Immediate ( self . resign_transmuted_fn_ptr ( bx, ptr, info) )
124+ }
125+
126+ /// Applies pointer-authentication type discriminator correction for a function pointer value
127+ /// being transmuted between two types.
128+ ///
129+ /// If the source and destination types have different type discriminator values, the pointer
130+ /// must be resigned using `llvm.ptrauth.resign` intrinsic.
131+ ///
132+ /// A discriminator value of `0` is used to represent non-function-pointer
133+ /// or "raw pointer" values:
134+ /// ```text
135+ /// static mut CPTR: *const u8 = 0 as *const u8;
136+ /// ... = mem::transmute::<*const u8, unsafe extern "C" fn()>(CPTR);
137+ /// ```
138+ /// where the source has no type discriminator.
139+ fn resign_transmuted_fn_ptr (
140+ & mut self ,
141+ bx : & mut Bx ,
142+ val : Bx :: Value ,
143+ info : TransmuteInfo < ' tcx > ,
144+ ) -> Bx :: Value {
145+ // Resigning can only happen in the context of function pointer type discrimination.
146+ assert ! ( self . cx. tcx( ) . sess. pointer_authentication_fn_ptr_type_discrimination( ) ) ;
147+
148+ let tcx = bx. tcx ( ) ;
149+
150+ let src_disc = ptrauth_compute_fn_ptr_type_discriminator_for ( tcx, info. src_ty ) . unwrap_or ( 0 ) ;
151+ let dst_disc = ptrauth_compute_fn_ptr_type_discriminator_for ( tcx, info. dst_ty ) . unwrap_or ( 0 ) ;
152+
153+ if src_disc == dst_disc {
154+ return val;
155+ }
156+
157+ debug ! ( "resign_transmuted_fn_ptr\t {:#x} -> {:#x}" , src_disc, dst_disc) ;
158+
159+ let key = self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_key ( ) . unwrap ( ) as u32 ;
160+ bx. ptrauth_resign ( val, key, src_disc. into ( ) , key, dst_disc. into ( ) )
161+ }
162+
163+ /// Walks through `#[repr(transparent)]` wrappers to find an underlying
164+ /// function pointer or function definition.
165+ ///
166+ /// Returns the corresponding layout if one is found, otherwise `None`.
167+ fn transparent_fn_ptr_layout (
168+ & self ,
169+ mut layout : TyAndLayout < ' tcx > ,
170+ ) -> Option < TyAndLayout < ' tcx > > {
171+ loop {
172+ match layout. ty . kind ( ) {
173+ ty:: FnPtr ( ..) | ty:: FnDef ( ..) => return Some ( layout) ,
174+
175+ ty:: Adt ( def, _) if def. repr ( ) . transparent ( ) => {
176+ layout = layout. field ( self . cx , 0 ) ;
177+ }
178+
179+ _ => return None ,
180+ }
181+ }
182+ }
183+
184+ /// Applies pointer-authentication type discriminator change during a transmute into a
185+ /// memory-backed place.
186+ ///
187+ /// Unlike the operand version, this path handles values stored in memory
188+ /// and therefore must unwrap #[repr(transparent)] wrapper types so that pointer
189+ /// authentication is based on the underlying function pointer type.
190+ ///
191+ /// Only immediate values are subject to ptrauth adjustment; other
192+ /// representations are passed through unchanged.
193+ fn ptrauth_codegen_transmute_place (
194+ & mut self ,
195+ bx : & mut Bx ,
196+ src : OperandRef < ' tcx , Bx :: Value > ,
197+ dst : PlaceRef < ' tcx , Bx :: Value > ,
198+ ) {
199+ debug ! (
200+ "ptrauth_codegen_transmute_place\t src={:?}, dst={:?}" ,
201+ src. layout. ty. kind( ) ,
202+ dst. layout. ty. kind( )
203+ ) ;
204+
205+ let info = TransmuteInfo {
206+ src_ty : self . transparent_fn_ptr_layout ( src. layout ) . map_or ( src. layout . ty , |l| l. ty ) ,
207+ dst_ty : self . transparent_fn_ptr_layout ( dst. layout ) . map_or ( dst. layout . ty , |l| l. ty ) ,
208+ } ;
209+
210+ let dest = dst. val . with_type ( src. layout ) ;
211+
212+ let val = match src. val {
213+ OperandValue :: Immediate ( v) => {
214+ let v = self . resign_transmuted_fn_ptr ( bx, v, info) ;
215+ OperandValue :: Immediate ( v)
216+ }
217+ other => other,
218+ } ;
219+
220+ OperandRef { val, layout : src. layout , move_annotation : None }
221+ . store_with_annotation ( bx, dest) ;
222+ }
223+
94224 #[ instrument( level = "trace" , skip( self , bx) ) ]
95225 pub ( crate ) fn codegen_rvalue (
96226 & mut self ,
@@ -179,8 +309,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
179309 mir:: Rvalue :: Cast (
180310 mir:: CastKind :: Transmute | mir:: CastKind :: Subtype ,
181311 ref operand,
182- _ty ,
312+ ty ,
183313 ) => {
314+ if self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_type_discrimination ( ) {
315+ let src_ty = operand. ty ( self . mir , self . cx . tcx ( ) ) ;
316+ let dst_ty = self . monomorphize ( ty) ;
317+
318+ if src_ty. is_fn_ptr ( ) || dst_ty. is_fn_ptr ( ) {
319+ let op = self . codegen_operand ( bx, operand) ;
320+ let cast = bx. cx ( ) . layout_of ( dst_ty) ;
321+
322+ let val = self . ptrauth_codegen_transmute_operand ( bx, op, cast) ;
323+
324+ OperandRef { val, layout : cast, move_annotation : None }
325+ . store_with_annotation ( bx, dest) ;
326+
327+ return ;
328+ }
329+ }
330+
184331 let src = self . codegen_operand ( bx, operand) ;
185332 self . codegen_transmute ( bx, src, dest) ;
186333 }
@@ -302,7 +449,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
302449 // Since in this path we have a place anyway, we can store or copy to it,
303450 // making sure we use the destination place's alignment even if the
304451 // source would normally have a higher one.
305- src. store_with_annotation ( bx, dst. val . with_type ( src. layout ) ) ;
452+
453+ if self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_type_discrimination ( ) {
454+ self . ptrauth_codegen_transmute_place ( bx, src, dst) ;
455+ } else {
456+ src. store_with_annotation ( bx, dst. val . with_type ( src. layout ) ) ;
457+ }
306458 }
307459 }
308460
@@ -316,6 +468,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
316468 operand : OperandRef < ' tcx , Bx :: Value > ,
317469 cast : TyAndLayout < ' tcx > ,
318470 ) -> OperandValue < Bx :: Value > {
471+ debug ! (
472+ "codegen_transmute_operand\t
473+ from_ty={:?} to_ty={:?} from_layout={:?} to_layout={:?} is fnptr=({}, {})" ,
474+ operand. layout. ty,
475+ cast. ty,
476+ operand. layout. backend_repr,
477+ cast. backend_repr,
478+ operand. layout. ty. is_fn_ptr( ) ,
479+ cast. ty. is_fn_ptr( )
480+ ) ;
319481 if let abi:: BackendRepr :: Memory { .. } = cast. backend_repr
320482 && !cast. is_zst ( )
321483 {
@@ -502,12 +664,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
502664 args. no_bound_vars ( ) . unwrap ( ) ,
503665 )
504666 . unwrap ( ) ;
505- OperandValue :: Immediate (
506- bx. get_fn_addr (
507- instance,
508- bx. sess ( ) . pointer_authentication_functions ( ) ,
509- ) ,
667+
668+ let schema = if bx. sess ( ) . pointer_authentication_fn_ptr_type_discrimination ( ) {
669+ ptrauth_clone_discriminated_schema_for (
670+ bx. tcx ( ) ,
671+ bx. sess ( ) . pointer_authentication_functions ( ) ,
672+ operand. layout . ty ,
510673 )
674+ } else {
675+ bx. sess ( ) . pointer_authentication_functions ( ) . clone ( )
676+ } ;
677+
678+ OperandValue :: Immediate ( bx. get_fn_addr ( instance, schema) )
511679 }
512680 _ => bug ! ( "{} cannot be reified to a fn ptr" , operand. layout. ty) ,
513681 }
@@ -521,10 +689,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
521689 args,
522690 ty:: ClosureKind :: FnOnce ,
523691 ) ;
692+ assert ! (
693+ !matches!(
694+ bx. cx( ) . tcx( ) . fn_sig( instance. def_id( ) ) . skip_binder( ) . abi( ) ,
695+ ExternAbi :: C { .. } | ExternAbi :: System { .. }
696+ )
697+ ) ;
524698 OperandValue :: Immediate (
699+ // A closure coerced to a function pointer retains the Rust
700+ // ABI. Pointer authentication only applies to extern
701+ // "C"/System ABI function pointer, hence pass None to
702+ // `get_fn_addr`.
525703 bx. cx ( ) . get_fn_addr (
526704 instance,
527- bx . sess ( ) . pointer_authentication_functions ( ) ,
705+ /* ptrauth_schema */ None ,
528706 ) ,
529707 )
530708 }
@@ -601,7 +779,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
601779 } )
602780 }
603781 mir:: CastKind :: Transmute | mir:: CastKind :: Subtype => {
604- self . codegen_transmute_operand ( bx, operand, cast)
782+ if self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_type_discrimination ( ) {
783+ self . ptrauth_codegen_transmute_operand ( bx, operand, cast)
784+ } else {
785+ self . codegen_transmute_operand ( bx, operand, cast)
786+ }
605787 }
606788 } ;
607789 OperandRef { val, layout : cast, move_annotation : None }
@@ -746,8 +928,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
746928 def : ty:: InstanceKind :: Shim ( ty:: ShimKind :: ThreadLocal ( def_id) ) ,
747929 args : ty:: GenericArgs :: empty ( ) ,
748930 } ;
749- let fn_ptr =
750- bx. get_fn_addr ( instance, bx. sess ( ) . pointer_authentication_functions ( ) ) ;
931+ // needs_thread_local_shim implies Windows/MSVC, for which pointer
932+ // authentication is not yet supported.
933+ assert ! ( !self . cx. tcx( ) . sess. pointer_authentication( ) ) ;
934+ let fn_ptr = bx. get_fn_addr ( instance, /* ptrauth_schema */ None ) ;
751935 let fn_abi = bx. fn_abi_of_instance ( instance, ty:: List :: empty ( ) ) ;
752936 let fn_ty = bx. fn_decl_backend_type ( fn_abi) ;
753937 let fn_attrs = if bx. tcx ( ) . def_kind ( instance. def_id ( ) ) . has_codegen_attrs ( ) {
0 commit comments