Skip to content

Commit 0f93e37

Browse files
committed
[PAC] Support type discriminators in static allocations
The codegen now walks the layout of static initializer types to find extern "C" function pointer fields, computes their type discriminators, and applies those discriminators when emitting authenticated function pointer relocations. Also make sure that type discrimination is never applied to init/fini entries.
1 parent 76aa0dd commit 0f93e37

5 files changed

Lines changed: 194 additions & 12 deletions

File tree

compiler/rustc_codegen_gcc/src/common.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use gccjit::{GlobalKind, LValue, RValue, ToRValue, Type};
22
use rustc_abi::Primitive::Pointer;
3-
use rustc_abi::{self as abi, HasDataLayout};
3+
use rustc_abi::{self as abi, HasDataLayout, Size};
44
use rustc_codegen_ssa::traits::{
55
BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods,
66
};
7+
use rustc_data_structures::fx::FxHashMap;
78
use rustc_middle::mir::Mutability;
89
use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar};
910
use rustc_middle::ty::layout::LayoutOf;
@@ -324,6 +325,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> {
324325
layout: abi::Scalar,
325326
ty: Type<'gcc>,
326327
_ptrauth_schema: Option<PointerAuthSchema>,
328+
_ptrauth_discriminators: Option<&FxHashMap<Size, u64>>,
327329
) -> RValue<'gcc> {
328330
let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() };
329331
match cv {

compiler/rustc_codegen_llvm/src/asm.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
164164
ConstScalar::Ptr(ptr, _) => {
165165
let (prov, _) = ptr.prov_and_relative_offset();
166166
let global_alloc = self.tcx.global_alloc(prov.alloc_id());
167-
let value = self.cx.alloc_to_backend(global_alloc, false, None).unwrap();
167+
let value =
168+
self.cx.alloc_to_backend(global_alloc, false, None, None).unwrap();
168169
inputs.push(value);
169170
op_idx.insert(idx, constraints.len());
170171
constraints.push("s".to_string());
@@ -453,8 +454,9 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> {
453454
ConstScalar::Ptr(ptr, _) => {
454455
let (prov, offset) = ptr.prov_and_relative_offset();
455456
let global_alloc = self.tcx.global_alloc(prov.alloc_id());
456-
let llval =
457-
self.alloc_to_backend(global_alloc, true, None).unwrap();
457+
let llval = self
458+
.alloc_to_backend(global_alloc, true, None, None)
459+
.unwrap();
458460

459461
self.add_compiler_used_global(llval);
460462
let symbol = llvm::build_string(|s| unsafe {

compiler/rustc_codegen_llvm/src/common.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ use std::borrow::Borrow;
44

55
use libc::{c_char, c_uint};
66
use rustc_abi::Primitive::Pointer;
7-
use rustc_abi::{self as abi, ExternAbi, HasDataLayout as _};
7+
use rustc_abi::{self as abi, ExternAbi, HasDataLayout as _, Size};
88
use rustc_ast::Mutability;
99
use rustc_codegen_ssa::common::TypeKind;
1010
use rustc_codegen_ssa::traits::*;
11+
use rustc_data_structures::fx::FxHashMap;
1112
use rustc_data_structures::stable_hash::{StableHash, StableHasher};
1213
use rustc_hashes::Hash128;
1314
use rustc_hir::def::DefKind;
@@ -183,6 +184,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
183184
global_alloc: GlobalAlloc<'tcx>,
184185
need_symbol_name: bool,
185186
ptrauth_schema: Option<PointerAuthSchema>,
187+
ptrauth_discriminators: Option<&FxHashMap<Size, u64>>,
186188
) -> Result<&'ll Value, u64> {
187189
let alloc = match global_alloc {
188190
GlobalAlloc::Function { instance, .. } => {
@@ -229,7 +231,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
229231
}
230232
};
231233

232-
let init = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No);
234+
let init = const_alloc_to_llvm(
235+
self,
236+
alloc.inner(),
237+
IsStatic::No,
238+
IsInitOrFini::No,
239+
ptrauth_discriminators,
240+
);
233241
let alloc = alloc.inner();
234242

235243
if need_symbol_name {
@@ -409,6 +417,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
409417
layout: abi::Scalar,
410418
llty: &'ll Type,
411419
ptrauth_schema: Option<PointerAuthSchema>,
420+
ptrauth_discriminators: Option<&FxHashMap<Size, u64>>,
412421
) -> &'ll Value {
413422
let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() };
414423
match cv {
@@ -425,7 +434,12 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {
425434
let (prov, offset) = ptr.prov_and_relative_offset();
426435
let global_alloc = self.tcx.global_alloc(prov.alloc_id());
427436
let base_addr_space = global_alloc.address_space(self);
428-
let base_addr = match self.alloc_to_backend(global_alloc, false, ptrauth_schema) {
437+
let base_addr = match self.alloc_to_backend(
438+
global_alloc,
439+
false,
440+
ptrauth_schema,
441+
ptrauth_discriminators,
442+
) {
429443
Ok(base_addr) => base_addr,
430444
Err(base_addr) => {
431445
let val = base_addr.wrapping_add(offset.bytes());

compiler/rustc_codegen_llvm/src/consts.rs

Lines changed: 165 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::ops::Range;
33
use rustc_abi::{Align, ExternAbi, HasDataLayout, Primitive, Scalar, Size, WrappingRange};
44
use rustc_codegen_ssa::common;
55
use rustc_codegen_ssa::traits::*;
6+
use rustc_data_structures::fx::FxHashMap;
67
use rustc_hir::LangItem;
78
use rustc_hir::attrs::Linkage;
89
use rustc_hir::def::DefKind;
@@ -13,8 +14,9 @@ use rustc_middle::mir::interpret::{
1314
read_target_uint,
1415
};
1516
use rustc_middle::mono::MonoItem;
17+
use rustc_middle::ptrauth::ptrauth_compute_fn_ptr_type_discriminator_for;
1618
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
17-
use rustc_middle::ty::{self, Instance};
19+
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
1820
use rustc_middle::{bug, span_bug};
1921
use rustc_span::Symbol;
2022
use rustc_target::spec::Arch;
@@ -37,11 +39,135 @@ pub(crate) enum IsInitOrFini {
3739
Yes,
3840
No,
3941
}
42+
43+
/// Recursively walks a type layout and records the offsets of all extern "C"
44+
/// function pointer fields together with their computed type discriminators.
45+
///
46+
/// Traversal currently supports:
47+
/// - references
48+
/// - direct function pointers
49+
/// - structs
50+
/// - tuples
51+
/// - arrays
52+
///
53+
/// Offsets are accumulated relative to the containing object.
54+
fn collect_fn_ptr_discriminators<'tcx>(
55+
tcx: TyCtxt<'tcx>,
56+
typing_env: ty::TypingEnv<'tcx>,
57+
ty: Ty<'tcx>,
58+
) -> FxHashMap<Size, u64> {
59+
let mut map = FxHashMap::default();
60+
61+
collect_fn_ptr_discriminators_inner(tcx, typing_env, ty, Size::ZERO, &mut map);
62+
63+
map
64+
}
65+
66+
fn collect_fn_ptr_discriminators_inner<'tcx>(
67+
tcx: TyCtxt<'tcx>,
68+
typing_env: ty::TypingEnv<'tcx>,
69+
ty: Ty<'tcx>,
70+
base_offset: Size,
71+
map: &mut FxHashMap<Size, u64>,
72+
) {
73+
// Direct function pointer.
74+
if let Some(disc) = ptrauth_compute_fn_ptr_type_discriminator_for(tcx, ty) {
75+
map.insert(base_offset, disc.into());
76+
77+
return;
78+
}
79+
80+
match ty.kind() {
81+
ty::Ref(_, pointee, _) => {
82+
collect_fn_ptr_discriminators_inner(tcx, typing_env, *pointee, base_offset, map);
83+
}
84+
ty::Adt(def, args) if def.is_struct() => {
85+
let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else {
86+
return;
87+
};
88+
89+
let variant = def.non_enum_variant();
90+
91+
for (idx, field_def) in variant.fields.iter_enumerated() {
92+
let field_ty = tcx.normalize_erasing_regions(typing_env, field_def.ty(tcx, args));
93+
94+
let field_offset = layout.fields.offset(idx.into());
95+
96+
collect_fn_ptr_discriminators_inner(
97+
tcx,
98+
typing_env,
99+
field_ty,
100+
base_offset + field_offset,
101+
map,
102+
);
103+
}
104+
}
105+
ty::Tuple(fields) => {
106+
let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else {
107+
return;
108+
};
109+
110+
for (idx, field_ty) in fields.iter().enumerate() {
111+
let field_offset = layout.fields.offset(idx);
112+
113+
collect_fn_ptr_discriminators_inner(
114+
tcx,
115+
typing_env,
116+
field_ty,
117+
base_offset + field_offset,
118+
map,
119+
);
120+
}
121+
}
122+
ty::Array(elem_ty, len) => {
123+
let count = match len.try_to_target_usize(tcx) {
124+
Some(v) => v,
125+
None => return,
126+
};
127+
128+
let Ok(elem_layout) = tcx.layout_of(typing_env.as_query_input(*elem_ty)) else {
129+
return;
130+
};
131+
132+
let stride = elem_layout.size;
133+
134+
// Collect discriminator of one element, so we don't have to recompute it for all the
135+
// elements in the array.
136+
let mut elem_map = FxHashMap::default();
137+
138+
collect_fn_ptr_discriminators_inner(
139+
tcx,
140+
typing_env,
141+
*elem_ty,
142+
Size::ZERO,
143+
&mut elem_map,
144+
);
145+
146+
// SAFETY: We immediately collect into a Vec and sort by offset.
147+
// The HashMap iteration order is irrelevant and must not affect determinism.
148+
#[allow(rustc::potential_query_instability)]
149+
let mut entries: Vec<(Size, u64)> = elem_map.into_iter().collect();
150+
entries.sort_unstable_by_key(|(offset, _)| *offset);
151+
152+
// Replicate for every array slot.
153+
for i in 0..count {
154+
let elem_base = base_offset + stride * i;
155+
156+
for (inner_offset, discr) in entries.iter().copied() {
157+
map.insert(elem_base + inner_offset, discr);
158+
}
159+
}
160+
}
161+
_ => {}
162+
}
163+
}
164+
40165
pub(crate) fn const_alloc_to_llvm<'ll>(
41166
cx: &CodegenCx<'ll, '_>,
42167
alloc: &Allocation,
43168
is_static: IsStatic,
44169
is_init_fini: IsInitOrFini,
170+
ptrauth_discriminators: Option<&FxHashMap<Size, u64>>,
45171
) -> &'ll Value {
46172
// We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or
47173
// integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be
@@ -121,14 +247,24 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
121247
as u64;
122248

123249
let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx);
124-
let schema = if cx.sess().pointer_authentication() {
250+
let mut schema = if cx.sess().pointer_authentication() {
125251
match is_init_fini {
126252
IsInitOrFini::Yes => cx.sess().pointer_authentication_init_fini(),
127253
IsInitOrFini::No => cx.sess().pointer_authentication_functions(),
128254
}
129255
} else {
130256
None
131257
};
258+
let discr =
259+
ptrauth_discriminators.as_ref().and_then(|m| m.get(&Size::from_bytes(offset as u64)));
260+
261+
// Init/fini entries must not participate in function pointer type discrimination, they use
262+
// a dedicated constant value (ptrauth_string_discriminator("init_fini") which is: 0xd9d4).
263+
if let (Some(schema), Some(discr)) = (schema.as_mut(), discr)
264+
&& is_init_fini == IsInitOrFini::No
265+
{
266+
schema.constant_discriminator = *discr as u16;
267+
}
132268
llvals.push(cx.scalar_to_backend_with_pac(
133269
InterpScalar::from_pointer(Pointer::new(prov, Size::from_bytes(ptr_offset)), &cx.tcx),
134270
Scalar::Initialized {
@@ -137,6 +273,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
137273
},
138274
cx.type_ptr_ext(address_space),
139275
schema,
276+
ptrauth_discriminators,
140277
));
141278
next_offset = offset + pointer_size_bytes;
142279
}
@@ -160,6 +297,15 @@ fn codegen_static_initializer<'ll, 'tcx>(
160297
cx: &CodegenCx<'ll, 'tcx>,
161298
def_id: DefId,
162299
) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> {
300+
let ptrauth_discriminators = if cx.sess().pointer_authentication_fn_ptr_type_discrimination() {
301+
let instance = Instance::mono(cx.tcx, def_id);
302+
let ty = instance.ty(cx.tcx, cx.typing_env());
303+
304+
Some(collect_fn_ptr_discriminators(cx.tcx, cx.typing_env(), ty))
305+
} else {
306+
None
307+
};
308+
163309
let alloc = cx.tcx.eval_static_initializer(def_id)?;
164310
let attrs = cx.tcx.codegen_fn_attrs(def_id);
165311
// FIXME(jchlanda) Decide if this could be better served by `ctor` crate. See the discussion
@@ -175,7 +321,16 @@ fn codegen_static_initializer<'ll, 'tcx>(
175321
}
176322
})
177323
.unwrap_or(IsInitOrFini::No);
178-
Ok((const_alloc_to_llvm(cx, alloc.inner(), IsStatic::Yes, is_in_init_fini), alloc))
324+
Ok((
325+
const_alloc_to_llvm(
326+
cx,
327+
alloc.inner(),
328+
IsStatic::Yes,
329+
is_in_init_fini,
330+
ptrauth_discriminators.as_ref(),
331+
),
332+
alloc,
333+
))
179334
}
180335

181336
fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) {
@@ -837,7 +992,13 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> {
837992
fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> &'ll Value {
838993
// FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the
839994
// same `ConstAllocation`?
840-
let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No);
995+
// FIXME(jchlanda): Add support for pointer authentication type discrimination.
996+
// `static_addr_of` only receives a `ConstAllocation`, so it does not have the type
997+
// information needed to compute function pointer type discriminators. We'll likely need
998+
// to either compute the discriminator map at callers that still know the Rust type, or
999+
// extend this API to accept the required type information. See
1000+
// `codegen_static_initializer` for an example of how the discriminator map is computed.
1001+
let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No, None);
8411002

8421003
let gv = self.static_addr_of_impl(cv, alloc.inner().align, kind);
8431004
// static_addr_of_impl returns the bare global variable, which might not be in the default

compiler/rustc_codegen_ssa/src/traits/consts.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use rustc_abi as abi;
2+
use rustc_abi::Size;
3+
use rustc_data_structures::fx::FxHashMap;
24
use rustc_middle::mir::interpret::Scalar;
35
use rustc_session::PointerAuthSchema;
46

@@ -40,14 +42,15 @@ pub trait ConstCodegenMethods: BackendTypes {
4042
fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option<u128>;
4143

4244
fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value {
43-
self.scalar_to_backend_with_pac(cv, layout, llty, None)
45+
self.scalar_to_backend_with_pac(cv, layout, llty, None, None)
4446
}
4547
fn scalar_to_backend_with_pac(
4648
&self,
4749
cv: Scalar,
4850
layout: abi::Scalar,
4951
llty: Self::Type,
5052
ptrauth_schema: Option<PointerAuthSchema>,
53+
ptrauth_discriminators: Option<&FxHashMap<Size, u64>>,
5154
) -> Self::Value;
5255

5356
fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value;

0 commit comments

Comments
 (0)