Skip to content

Commit c035be4

Browse files
committed
[PAC] Function pointer type discrimination for transmutes
Implement pointer authentication domain handling for function pointer transmutes. When function pointer type discrimination is enabled, transmuting between function pointer types with different authentication domains now re-signs the pointer using the appropriate discriminator.
1 parent 1ab8f04 commit c035be4

1 file changed

Lines changed: 196 additions & 12 deletions

File tree

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 196 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use std::assert_matches;
22

33
use itertools::Itertools as _;
4-
use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT};
4+
use rustc_abi::{self as abi, BackendRepr, ExternAbi, FIRST_VARIANT};
55
use rustc_index::IndexVec;
6+
use rustc_middle::ptrauth::{
7+
ptrauth_clone_discriminated_schema_for, ptrauth_compute_fn_ptr_type_discriminator_for,
8+
};
69
use rustc_middle::ty::adjustment::PointerCoercion;
710
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
811
use rustc_middle::ty::{self, Instance, Mutability, Ty, TyCtxt};
@@ -17,6 +20,13 @@ use crate::common::{IntPredicate, TypeKind};
1720
use crate::traits::*;
1821
use 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+
2030
impl<'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\tsrc={:?}, 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

Comments
 (0)