Skip to content

Commit 589c793

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 5b223de commit 589c793

1 file changed

Lines changed: 205 additions & 12 deletions

File tree

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 205 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use itertools::Itertools as _;
22
use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT};
33
use rustc_index::IndexVec;
4+
use rustc_middle::ptrauth::{
5+
build_fn_ptr_type_discriminator_input_from_ty, compute_fn_ptr_type_discriminator,
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,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\tsrc={:?}, 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

Comments
 (0)