11use itertools:: Itertools as _;
2- use rustc_abi:: { self as abi, BackendRepr , FIRST_VARIANT } ;
2+ use rustc_abi:: { self as abi, BackendRepr , ExternAbi , FIRST_VARIANT } ;
33use rustc_index:: IndexVec ;
4+ use rustc_middle:: ptrauth:: {
5+ clone_discriminated_ptrauth_schema_for, compute_fn_ptr_type_discriminator_for,
6+ } ;
47use rustc_middle:: ty:: adjustment:: PointerCoercion ;
58use rustc_middle:: ty:: layout:: { HasTyCtxt , HasTypingEnv , LayoutOf , TyAndLayout } ;
69use rustc_middle:: ty:: { self , Instance , Mutability , Ty , TyCtxt } ;
@@ -15,6 +18,13 @@ use crate::common::{IntPredicate, TypeKind};
1518use crate :: traits:: * ;
1619use crate :: { MemFlags , base} ;
1720
21+ /// Type metadata used when applying pointer authentication semantics during
22+ /// transmute lowering.
23+ struct TransmuteInfo < ' tcx > {
24+ src_ty : Ty < ' tcx > ,
25+ dst_ty : Ty < ' tcx > ,
26+ }
27+
1828impl < ' a , ' tcx , Bx : BuilderMethods < ' a , ' tcx > > FunctionCx < ' a , ' tcx , Bx > {
1929 fn try_codegen_const_aggregate_as_immediate (
2030 & mut self ,
@@ -89,6 +99,128 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
8999 true
90100 }
91101
102+ /// Applies pointer-authentication-aware semantic transmute, that is
103+ /// ensuring that when a function pointer is transmuted between two types
104+ /// that map to different authentication domains (discriminators), the
105+ /// resulting pointer is re-signed appropriately.
106+ ///
107+ /// Only SSA `OperandValue::Immediate` values are eligible for this path.
108+ fn codegen_semantic_transmute_operand (
109+ & mut self ,
110+ bx : & mut Bx ,
111+ operand : OperandRef < ' tcx , Bx :: Value > ,
112+ cast : TyAndLayout < ' tcx > ,
113+ ) -> OperandValue < Bx :: Value > {
114+ let val = self . codegen_transmute_operand ( bx, operand, cast) ;
115+
116+ let OperandValue :: Immediate ( ptr) = val else {
117+ return val;
118+ } ;
119+
120+ let info = TransmuteInfo { src_ty : operand. layout . ty , dst_ty : cast. ty } ;
121+
122+ OperandValue :: Immediate ( self . resign_transmuted_fn_ptr ( bx, ptr, info) )
123+ }
124+
125+ /// Applies pointer-authentication domain correction for a function pointer
126+ /// value being transmuted between two types.
127+ ///
128+ /// The "domain" is defined by the function pointer type discriminator. If
129+ /// the source and destination types map to different discriminator values,
130+ /// the pointer 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 domain" 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 authentication domain.
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 = compute_fn_ptr_type_discriminator_for ( tcx, info. src_ty ) . unwrap_or ( 0 ) ;
151+ let dst_disc = 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 domain 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 codegen_semantic_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+ "codegen_semantic_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+
92224 #[ instrument( level = "trace" , skip( self , bx) ) ]
93225 pub ( crate ) fn codegen_rvalue (
94226 & mut self ,
@@ -180,8 +312,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
180312 mir:: Rvalue :: Cast (
181313 mir:: CastKind :: Transmute | mir:: CastKind :: Subtype ,
182314 ref operand,
183- _ty ,
315+ ty ,
184316 ) => {
317+ if self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_type_discrimination ( ) {
318+ let src_ty = operand. ty ( self . mir , self . cx . tcx ( ) ) ;
319+ let dst_ty = self . monomorphize ( ty) ;
320+
321+ if src_ty. is_fn_ptr ( ) || dst_ty. is_fn_ptr ( ) {
322+ let op = self . codegen_operand ( bx, operand) ;
323+ let cast = bx. cx ( ) . layout_of ( dst_ty) ;
324+
325+ let val = self . codegen_semantic_transmute_operand ( bx, op, cast) ;
326+
327+ OperandRef { val, layout : cast, move_annotation : None }
328+ . store_with_annotation ( bx, dest) ;
329+
330+ return ;
331+ }
332+ }
333+
185334 let src = self . codegen_operand ( bx, operand) ;
186335 self . codegen_transmute ( bx, src, dest) ;
187336 }
@@ -316,7 +465,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
316465 // Since in this path we have a place anyway, we can store or copy to it,
317466 // making sure we use the destination place's alignment even if the
318467 // source would normally have a higher one.
319- src. store_with_annotation ( bx, dst. val . with_type ( src. layout ) ) ;
468+
469+ if self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_type_discrimination ( ) {
470+ self . codegen_semantic_transmute_place ( bx, src, dst) ;
471+ } else {
472+ src. store_with_annotation ( bx, dst. val . with_type ( src. layout ) ) ;
473+ }
320474 }
321475 }
322476
@@ -330,6 +484,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
330484 operand : OperandRef < ' tcx , Bx :: Value > ,
331485 cast : TyAndLayout < ' tcx > ,
332486 ) -> OperandValue < Bx :: Value > {
487+ debug ! (
488+ "codegen_transmute_operand\t
489+ from_ty={:?} to_ty={:?} from_layout={:?} to_layout={:?} is fnptr=({}, {})" ,
490+ operand. layout. ty,
491+ cast. ty,
492+ operand. layout. backend_repr,
493+ cast. backend_repr,
494+ operand. layout. ty. is_fn_ptr( ) ,
495+ cast. ty. is_fn_ptr( )
496+ ) ;
333497 if let abi:: BackendRepr :: Memory { .. } = cast. backend_repr
334498 && !cast. is_zst ( )
335499 {
@@ -515,12 +679,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
515679 args. no_bound_vars ( ) . unwrap ( ) ,
516680 )
517681 . unwrap ( ) ;
518- OperandValue :: Immediate (
519- bx. get_fn_addr (
520- instance,
521- bx. sess ( ) . pointer_authentication_functions ( ) ,
522- ) ,
682+
683+ let schema = if bx. sess ( ) . pointer_authentication_fn_ptr_type_discrimination ( ) {
684+ clone_discriminated_ptrauth_schema_for (
685+ bx. tcx ( ) ,
686+ bx. sess ( ) . pointer_authentication_functions ( ) ,
687+ operand. layout . ty ,
523688 )
689+ } else {
690+ bx. sess ( ) . pointer_authentication_functions ( ) . clone ( )
691+ } ;
692+
693+ OperandValue :: Immediate ( bx. get_fn_addr ( instance, schema) )
524694 }
525695 _ => bug ! ( "{} cannot be reified to a fn ptr" , operand. layout. ty) ,
526696 }
@@ -534,10 +704,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
534704 args,
535705 ty:: ClosureKind :: FnOnce ,
536706 ) ;
707+ assert ! (
708+ !matches!(
709+ bx. cx( ) . tcx( ) . fn_sig( instance. def_id( ) ) . skip_binder( ) . abi( ) ,
710+ ExternAbi :: C { .. } | ExternAbi :: System { .. }
711+ )
712+ ) ;
537713 OperandValue :: Immediate (
714+ // A closure coerced to a function pointer retains the Rust
715+ // ABI. Pointer authentication only applies to extern
716+ // "C"/System ABI function pointer, hence pass None to
717+ // `get_fn_addr`.
538718 bx. cx ( ) . get_fn_addr (
539719 instance,
540- bx . sess ( ) . pointer_authentication_functions ( ) ,
720+ /* pointer_auth_schema */ None ,
541721 ) ,
542722 )
543723 }
@@ -614,7 +794,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
614794 } )
615795 }
616796 mir:: CastKind :: Transmute | mir:: CastKind :: Subtype => {
617- self . codegen_transmute_operand ( bx, operand, cast)
797+ if self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_type_discrimination ( ) {
798+ self . codegen_semantic_transmute_operand ( bx, operand, cast)
799+ } else {
800+ self . codegen_transmute_operand ( bx, operand, cast)
801+ }
618802 }
619803 } ;
620804 OperandRef { val, layout : cast, move_annotation : None }
@@ -759,8 +943,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
759943 def : ty:: InstanceKind :: Shim ( ty:: ShimKind :: ThreadLocal ( def_id) ) ,
760944 args : ty:: GenericArgs :: empty ( ) ,
761945 } ;
762- let fn_ptr =
763- bx. get_fn_addr ( instance, bx. sess ( ) . pointer_authentication_functions ( ) ) ;
946+ // This is the address of a compiler-generated TLS shim function. It is not an
947+ // externally visible function pointer and does not require function pointer
948+ // authentication signing.
949+ let fn_ptr = bx. get_fn_addr ( instance, /* pointer_auth_schema */ None ) ;
764950 let fn_abi = bx. fn_abi_of_instance ( instance, ty:: List :: empty ( ) ) ;
765951 let fn_ty = bx. fn_decl_backend_type ( fn_abi) ;
766952 let fn_attrs = if bx. tcx ( ) . def_kind ( instance. def_id ( ) ) . has_codegen_attrs ( ) {
0 commit comments