Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 123 additions & 28 deletions compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,34 +129,8 @@ fn encode_const<'tcx>(
// Element type
s.push_str(&encode_ty(tcx, cv.ty, dict, options));

// The only allowed types of const values are bool, u8, u16, u32,
// u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The
// bool value false is encoded as 0 and true as 1.
match cv.ty.kind() {
ty::Int(ity) => {
let bits = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in cfi");
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
if val < 0 {
s.push('n');
}
let _ = write!(s, "{val}");
}
ty::Uint(_) => {
let val = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in cfi");
let _ = write!(s, "{val}");
}
ty::Bool => {
let val = cv.try_to_bool().expect("expected monomorphic const in cfi");
let _ = write!(s, "{val}");
}
_ => {
bug!("encode_const: unexpected type `{:?}`", cv.ty);
}
}
// Element value
s.push_str(&encode_const_value(tcx, cv, dict, options));
}

_ => {
Expand All @@ -172,6 +146,127 @@ fn encode_const<'tcx>(
s
}

/// Encodes a const value using the Itanium C++ ABI as the element value of a literal argument (see
/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
fn encode_const_value<'tcx>(
tcx: TyCtxt<'tcx>,
cv: ty::Value<'tcx>,
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
options: EncodeTyOptions,
) -> String {
let mut s = String::new();

match cv.ty.kind() {
// Primitive types

// The bool value false is encoded as 0 and true as 1.
ty::Bool => {
let val = cv.try_to_bool().expect("expected monomorphic const in cfi");
s.push(if val { '1' } else { '0' });
}

// Integer values are encoded as their decimal values, with negative values preceded by n.
ty::Int(ity) => {
let bits = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in cfi");
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
if val < 0 {
s.push('n');
}
let _ = write!(s, "{}", val.unsigned_abs());
}

ty::Uint(..) => {
let val = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in cfi");
let _ = write!(s, "{val}");
}

// char values are encoded as their Unicode scalar values (i.e., as their decimal u32
// values).
ty::Char => {
let val = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in cfi");
let _ = write!(s, "{val}");
}

// str values are encoded as their UTF-8 encodings in hexadecimal.
ty::Str => {
// Hide the str type behind a reference for try_to_raw_bytes (i.e., the valtree of a
// str value is the valtree of its reference).
let ref_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, cv.ty);
let cv = ty::Value { ty: ref_ty, valtree: cv.valtree };
let bytes = cv.try_to_raw_bytes(tcx).expect("expected monomorphic const in cfi");
for byte in bytes {
let _ = write!(s, "{byte:02x}");
}
}

// Sequence types
// Array, slice, and tuple values are encoded as their element values as literal arguments.
ty::Array(..) | ty::Slice(..) | ty::Tuple(..) => {
Comment thread
bjorn3 marked this conversation as resolved.
for field in cv.to_branch() {
let ty::ConstKind::Value(field_cv) = field.kind() else {
bug!("encode_const_value: unexpected kind `{:?}`", field.kind());
};
s.push_str(&encode_const(tcx, *field, field_cv.ty, dict, options));
}
}

// User-defined types
// Struct and enum values are encoded as their field values as literal arguments, preceded
// by V<variant-index> for enum values.
ty::Adt(adt_def, ..) => {
let contents = cv.destructure_adt_const();
if adt_def.is_enum() {
let _ = write!(s, "V{}", contents.variant.as_u32());
}
for field in contents.fields {
let ty::ConstKind::Value(field_cv) = field.kind() else {
bug!("encode_const_value: unexpected kind `{:?}`", field.kind());
};
s.push_str(&encode_const(tcx, *field, field_cv.ty, dict, options));
}
}

// Pointer types
// Reference values are encoded as the values of their referents (i.e., the valtree of a
// reference value is the valtree of its referent).
ty::Ref(_, ty0, ..) => {
let cv = ty::Value { ty: *ty0, valtree: cv.valtree };
s.push_str(&encode_const_value(tcx, cv, dict, options));
}

// Unexpected types
ty::Float(..)
| ty::Never
| ty::Foreign(..)
| ty::Pat(..)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::RawPtr(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Dynamic(..)
| ty::UnsafeBinder(..)
| ty::Param(..)
| ty::Alias(..)
| ty::Bound(..)
| ty::Error(..)
| ty::Infer(..)
| ty::Placeholder(..) => {
bug!("encode_const_value: unexpected type `{:?}`", cv.ty);
}
}

s
}

/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
/// Rust types that are not used at the FFI boundary.
fn encode_fnsig<'tcx>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 -C unsafe-allow-abi-mismatch=sanitizer

#![crate_type = "lib"]
#![feature(adt_const_params)]
#![feature(type_alias_impl_trait)]
#![feature(unsized_const_params)]
#![allow(incomplete_features)]

extern crate core;

use std::marker::ConstParamTy;

pub type Type1 = impl Send;

#[define_opaque(Type1)]
Expand All @@ -27,6 +32,52 @@ pub fn foo2(_: Type1, _: Type1) {}
pub fn foo3(_: Type1, _: Type1, _: Type1) {}
// CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}

#[derive(PartialEq, Eq, ConstParamTy)]
pub struct Struct1 {
pub x: u16,
pub y: u16,
}

#[derive(PartialEq, Eq, ConstParamTy)]
pub enum Enum1 {
Variant1,
Variant2(u8),
}

pub struct BoolHolder<const B: bool>;
pub struct IntHolder<const I: i32>;
pub struct CharHolder<const C: char>;
pub struct StrHolder<const S: &'static str>;
pub struct StructHolder<const S: Struct1>;
pub struct EnumHolder<const E: Enum1>;
pub struct ArrayHolder<const A: [u16; 2]>;
pub struct TupleHolder<const T: (u16, bool)>;

pub fn foo4(_: &BoolHolder<true>) {}
// CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo5(_: &IntHolder<-1>) {}
// CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo6(_: &CharHolder<'x'>) {}
// CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo7(_: &StrHolder<"hello">) {}
// CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo8(_: &StructHolder<{ Struct1 { x: 1, y: 2 } }>) {}
// CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo9(_: &EnumHolder<{ Enum1::Variant2(5) }>) {}
// CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo10(_: &ArrayHolder<{ [3, 4] }>) {}
// CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
pub fn foo11(_: &TupleHolder<{ (6, true) }>) {}
// CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}

// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooIu3i32Lu5usize32EEE"}
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooIu3i32Lu5usize32EES2_E"}
// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooIu3i32Lu5usize32EES2_S2_E"}
// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}10BoolHolderILb1EEEE"}
// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}9IntHolderILu3i32n1EEEE"}
// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}10CharHolderILu4char120EEEE"}
// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}9StrHolderILu3refIu3strE68656c6c6fEEEE"}
// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}12StructHolderILu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}7Struct1Lu3u161ELS0_2EEEEE"}
// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}10EnumHolderILu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Enum1V1Lu2u85EEEEE"}
// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}11ArrayHolderILA2u3u16LS_3ELS_4EEEEE"}
// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}11TupleHolderILu5tupleIu3u16bELS_6ELb1EEEEE"}
110 changes: 110 additions & 0 deletions tests/ui/sanitizer/cfi/const-generics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Verifies that functions with types with const generics as argument types can
// be called through function pointers.
//
//@ needs-sanitizer-cfi
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
//@ only-linux
//@ ignore-backends: gcc
//@ compile-flags: -Ctarget-feature=-crt-static -Ccodegen-units=1 -Clto -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=cfi -Cunsafe-allow-abi-mismatch=sanitizer
//@ run-pass

#![feature(adt_const_params)]
#![feature(unsized_const_params)]
#![allow(incomplete_features)]

use std::marker::ConstParamTy;

#[derive(PartialEq, Eq, ConstParamTy)]
struct Struct2 {
x: u16,
y: u16,
}

#[derive(PartialEq, Eq, ConstParamTy)]
enum Enum1 {
Variant1,
Variant2(u8),
}

struct Struct1<const N: usize>([i32; N]);
struct BoolHolder<const B: bool>(bool);
struct IntHolder<const I: i32>(i32);
struct CharHolder<const C: char>(char);
struct StrHolder<const S: &'static str>(&'static str);
struct StructHolder<const S: Struct2>(Struct2);
struct EnumHolder<const E: Enum1>(Enum1);
struct ArrayHolder<const A: [u16; 2]>([u16; 2]);
struct TupleHolder<const T: (u16, bool)>((u16, bool));

fn foo1(x: Struct1<2>) {
assert_eq!(x.0, [1, 2]);
}

fn foo2(x: &Struct1<4>) {
assert_eq!(x.0, [1, 2, 3, 4]);
}

fn foo3(x: BoolHolder<true>) {
assert!(x.0);
}

fn foo4(x: IntHolder<-1>) {
assert_eq!(x.0, -1);
}

fn foo5(x: CharHolder<'x'>) {
assert_eq!(x.0, 'x');
}

fn foo6(x: StrHolder<"hello">) {
assert_eq!(x.0, "hello");
}

fn foo7(x: StructHolder<{ Struct2 { x: 1, y: 2 } }>) {
assert_eq!(x.0.x, 1);
assert_eq!(x.0.y, 2);
}

fn foo8(x: EnumHolder<{ Enum1::Variant1 }>) {
assert!(matches!(x.0, Enum1::Variant1));
}

fn foo9(x: EnumHolder<{ Enum1::Variant2(5) }>) {
match x.0 {
Enum1::Variant1 => unreachable!(),
Enum1::Variant2(v) => assert_eq!(v, 5),
}
}

fn foo10(x: ArrayHolder<{ [3, 4] }>) {
assert_eq!(x.0, [3, 4]);
}

fn foo11(x: TupleHolder<{ (6, true) }>) {
assert_eq!(x.0, (6, true));
}

fn main() {
let f: fn(Struct1<2>) = foo1;
f(Struct1([1, 2]));
let f: fn(&Struct1<4>) = foo2;
f(&Struct1([1, 2, 3, 4]));
let f: fn(BoolHolder<true>) = foo3;
f(BoolHolder(true));
let f: fn(IntHolder<-1>) = foo4;
f(IntHolder(-1));
let f: fn(CharHolder<'x'>) = foo5;
f(CharHolder('x'));
let f: fn(StrHolder<"hello">) = foo6;
f(StrHolder("hello"));
let f: fn(StructHolder<{ Struct2 { x: 1, y: 2 } }>) = foo7;
f(StructHolder(Struct2 { x: 1, y: 2 }));
let f: fn(EnumHolder<{ Enum1::Variant1 }>) = foo8;
f(EnumHolder(Enum1::Variant1));
let f: fn(EnumHolder<{ Enum1::Variant2(5) }>) = foo9;
f(EnumHolder(Enum1::Variant2(5)));
let f: fn(ArrayHolder<{ [3, 4] }>) = foo10;
f(ArrayHolder([3, 4]));
let f: fn(TupleHolder<{ (6, true) }>) = foo11;
f(TupleHolder((6, true)));
}
Loading