Skip to content

Commit f19ee3f

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 e08ffe7 commit f19ee3f

1 file changed

Lines changed: 198 additions & 12 deletions

File tree

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 198 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use itertools::Itertools as _;
2-
use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT};
2+
use rustc_abi::{self as abi, BackendRepr, ExternAbi, FIRST_VARIANT};
33
use rustc_index::IndexVec;
4+
use rustc_middle::ptrauth::{
5+
clone_discriminated_ptrauth_schema_for, compute_fn_ptr_type_discriminator_for,
6+
};
47
use rustc_middle::ty::adjustment::PointerCoercion;
58
use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout};
69
use rustc_middle::ty::{self, Instance, Mutability, Ty, TyCtxt};
@@ -15,6 +18,13 @@ use crate::common::{IntPredicate, TypeKind};
1518
use crate::traits::*;
1619
use 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+
1828
impl<'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\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+
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

Comments
 (0)