11use itertools:: Itertools as _;
22use rustc_abi:: { self as abi, BackendRepr , FIRST_VARIANT } ;
33use rustc_index:: IndexVec ;
4+ use rustc_middle:: ptrauth:: {
5+ build_fn_ptr_type_discriminator_input_from_ty, compute_fn_ptr_type_discriminator,
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,135 @@ 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 re-signed 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+ let tcx = bx. tcx ( ) ;
146+
147+ let src_input = build_fn_ptr_type_discriminator_input_from_ty ( tcx, info. src_ty ) ;
148+ let dst_input = build_fn_ptr_type_discriminator_input_from_ty ( tcx, info. dst_ty ) ;
149+
150+ let src_disc = match src_input {
151+ Some ( src) => compute_fn_ptr_type_discriminator ( tcx, & src) ,
152+ None => 0 ,
153+ } ;
154+
155+ let dst_disc = match dst_input {
156+ Some ( dst) => compute_fn_ptr_type_discriminator ( tcx, & dst) ,
157+ None => 0 ,
158+ } ;
159+
160+ if src_disc == dst_disc {
161+ return val;
162+ }
163+
164+ debug ! ( "resign_transmuted_fn_ptr\t {:#x} -> {:#x}" , src_disc, dst_disc) ;
165+
166+ let key = self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_key ( ) . unwrap ( ) as u32 ;
167+ bx. ptrauth_resign ( val, key, src_disc, key, dst_disc)
168+ }
169+
170+ /// Walks through `#[repr(transparent)]` wrappers to find an underlying
171+ /// function pointer or function definition.
172+ ///
173+ /// Returns the corresponding layout if one is found, otherwise `None`.
174+ fn transparent_fn_ptr_layout (
175+ & self ,
176+ mut layout : TyAndLayout < ' tcx > ,
177+ ) -> Option < TyAndLayout < ' tcx > > {
178+ loop {
179+ match layout. ty . kind ( ) {
180+ ty:: FnPtr ( ..) | ty:: FnDef ( ..) => return Some ( layout) ,
181+
182+ ty:: Adt ( def, _) if def. repr ( ) . transparent ( ) => {
183+ layout = layout. field ( self . cx , 0 ) ;
184+ }
185+
186+ _ => return None ,
187+ }
188+ }
189+ }
190+
191+ /// Applies pointer-authentication domain change during a transmute into a
192+ /// memory-backed place.
193+ ///
194+ /// Unlike the operand version, this path handles values stored in memory
195+ /// and therefore must unwrap #[repr(transparent)] wrapper types so that pointer
196+ /// authentication is based on the underlying function pointer type.
197+ ///
198+ /// Only immediate values are subject to ptrauth adjustment; other
199+ /// representations are passed through unchanged.
200+ fn codegen_semantic_transmute_place (
201+ & mut self ,
202+ bx : & mut Bx ,
203+ src : OperandRef < ' tcx , Bx :: Value > ,
204+ dst : PlaceRef < ' tcx , Bx :: Value > ,
205+ ) {
206+ debug ! (
207+ "codegen_semantic_transmute_place\t src={:?}, dst={:?}" ,
208+ src. layout. ty. kind( ) ,
209+ dst. layout. ty. kind( )
210+ ) ;
211+
212+ let info = TransmuteInfo {
213+ src_ty : self . transparent_fn_ptr_layout ( src. layout ) . map_or ( src. layout . ty , |l| l. ty ) ,
214+ dst_ty : self . transparent_fn_ptr_layout ( dst. layout ) . map_or ( dst. layout . ty , |l| l. ty ) ,
215+ } ;
216+
217+ let dest = dst. val . with_type ( src. layout ) ;
218+
219+ let val = match src. val {
220+ OperandValue :: Immediate ( v) => {
221+ let v = self . resign_transmuted_fn_ptr ( bx, v, info) ;
222+ OperandValue :: Immediate ( v)
223+ }
224+ other => other,
225+ } ;
226+
227+ OperandRef { val, layout : src. layout , move_annotation : None }
228+ . store_with_annotation ( bx, dest) ;
229+ }
230+
92231 #[ instrument( level = "trace" , skip( self , bx) ) ]
93232 pub ( crate ) fn codegen_rvalue (
94233 & mut self ,
@@ -180,8 +319,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
180319 mir:: Rvalue :: Cast (
181320 mir:: CastKind :: Transmute | mir:: CastKind :: Subtype ,
182321 ref operand,
183- _ty ,
322+ ty ,
184323 ) => {
324+ if self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_type_discrimination ( ) {
325+ let src_ty = operand. ty ( self . mir , self . cx . tcx ( ) ) ;
326+ let dst_ty = self . monomorphize ( ty) ;
327+
328+ if src_ty. is_fn_ptr ( ) || dst_ty. is_fn_ptr ( ) {
329+ let op = self . codegen_operand ( bx, operand) ;
330+ let cast = bx. cx ( ) . layout_of ( dst_ty) ;
331+
332+ let val = self . codegen_semantic_transmute_operand ( bx, op, cast) ;
333+
334+ OperandRef { val, layout : cast, move_annotation : None }
335+ . store_with_annotation ( bx, dest) ;
336+
337+ return ;
338+ }
339+ }
340+
185341 let src = self . codegen_operand ( bx, operand) ;
186342 self . codegen_transmute ( bx, src, dest) ;
187343 }
@@ -316,7 +472,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
316472 // Since in this path we have a place anyway, we can store or copy to it,
317473 // making sure we use the destination place's alignment even if the
318474 // source would normally have a higher one.
319- src. store_with_annotation ( bx, dst. val . with_type ( src. layout ) ) ;
475+
476+ if self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_type_discrimination ( ) {
477+ self . codegen_semantic_transmute_place ( bx, src, dst) ;
478+ } else {
479+ src. store_with_annotation ( bx, dst. val . with_type ( src. layout ) ) ;
480+ }
320481 }
321482 }
322483
@@ -330,6 +491,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
330491 operand : OperandRef < ' tcx , Bx :: Value > ,
331492 cast : TyAndLayout < ' tcx > ,
332493 ) -> OperandValue < Bx :: Value > {
494+ debug ! (
495+ "codegen_transmute_operand\t
496+ from_ty={:?} to_ty={:?} from_layout={:?} to_layout={:?} is fnptr=({}, {})" ,
497+ operand. layout. ty,
498+ cast. ty,
499+ operand. layout. backend_repr,
500+ cast. backend_repr,
501+ operand. layout. ty. is_fn_ptr( ) ,
502+ cast. ty. is_fn_ptr( )
503+ ) ;
333504 if let abi:: BackendRepr :: Memory { .. } = cast. backend_repr
334505 && !cast. is_zst ( )
335506 {
@@ -515,12 +686,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
515686 args. no_bound_vars ( ) . unwrap ( ) ,
516687 )
517688 . unwrap ( ) ;
518- OperandValue :: Immediate (
519- bx. get_fn_addr (
520- instance,
521- bx. sess ( ) . pointer_authentication_functions ( ) ,
522- ) ,
523- )
689+ let mut schema = bx. sess ( ) . pointer_authentication_functions ( ) ;
690+
691+ if let Some ( ref mut s) = schema {
692+ if bx. sess ( ) . pointer_authentication_fn_ptr_type_discrimination ( ) {
693+ if let Some ( input) = build_fn_ptr_type_discriminator_input_from_ty (
694+ bx. tcx ( ) ,
695+ operand. layout . ty ,
696+ ) {
697+ s. constant_discriminator =
698+ compute_fn_ptr_type_discriminator (
699+ bx. tcx ( ) ,
700+ & input,
701+ ) as u16 ;
702+ }
703+ }
704+ }
705+
706+ OperandValue :: Immediate ( bx. get_fn_addr ( instance, schema) )
524707 }
525708 _ => bug ! ( "{} cannot be reified to a fn ptr" , operand. layout. ty) ,
526709 }
@@ -535,9 +718,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
535718 ty:: ClosureKind :: FnOnce ,
536719 ) ;
537720 OperandValue :: Immediate (
721+ // A closure coerced to a function pointer retains the Rust
722+ // ABI. Pointer authentication only applies to extern
723+ // "C"/System ABI function pointer, hence pass None to
724+ // `get_fn_addr`.
538725 bx. cx ( ) . get_fn_addr (
539726 instance,
540- bx . sess ( ) . pointer_authentication_functions ( ) ,
727+ None ,
541728 ) ,
542729 )
543730 }
@@ -614,7 +801,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
614801 } )
615802 }
616803 mir:: CastKind :: Transmute | mir:: CastKind :: Subtype => {
617- self . codegen_transmute_operand ( bx, operand, cast)
804+ if self . cx . tcx ( ) . sess . pointer_authentication_fn_ptr_type_discrimination ( ) {
805+ self . codegen_semantic_transmute_operand ( bx, operand, cast)
806+ } else {
807+ self . codegen_transmute_operand ( bx, operand, cast)
808+ }
618809 }
619810 } ;
620811 OperandRef { val, layout : cast, move_annotation : None }
@@ -759,8 +950,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
759950 def : ty:: InstanceKind :: Shim ( ty:: ShimKind :: ThreadLocal ( def_id) ) ,
760951 args : ty:: GenericArgs :: empty ( ) ,
761952 } ;
762- let fn_ptr =
763- bx. get_fn_addr ( instance, bx. sess ( ) . pointer_authentication_functions ( ) ) ;
953+ // This is the address of a compiler-generated TLS shim function. It is not an
954+ // externally visible function pointer and does not require function pointer
955+ // authentication signing.
956+ let fn_ptr = bx. get_fn_addr ( instance, None ) ;
764957 let fn_abi = bx. fn_abi_of_instance ( instance, ty:: List :: empty ( ) ) ;
765958 let fn_ty = bx. fn_decl_backend_type ( fn_abi) ;
766959 let fn_attrs = if bx. tcx ( ) . def_kind ( instance. def_id ( ) ) . has_codegen_attrs ( ) {
0 commit comments