diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 873ed9bb10398..2869da32604a2 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -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)); } _ => { @@ -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 +/// ). +fn encode_const_value<'tcx>( + tcx: TyCtxt<'tcx>, + cv: ty::Value<'tcx>, + dict: &mut FxHashMap, 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(..) => { + 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 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>( diff --git a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs index cf26c17af1ed3..98591b0d4f1d2 100644 --- a/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs +++ b/tests/codegen-llvm/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-const-generics.rs @@ -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)] @@ -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; +pub struct IntHolder; +pub struct CharHolder; +pub struct StrHolder; +pub struct StructHolder; +pub struct EnumHolder; +pub struct ArrayHolder; +pub struct TupleHolder; + +pub fn foo4(_: &BoolHolder) {} +// 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"} diff --git a/tests/ui/sanitizer/cfi/const-generics.rs b/tests/ui/sanitizer/cfi/const-generics.rs new file mode 100644 index 0000000000000..42fff233dd84b --- /dev/null +++ b/tests/ui/sanitizer/cfi/const-generics.rs @@ -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([i32; N]); +struct BoolHolder(bool); +struct IntHolder(i32); +struct CharHolder(char); +struct StrHolder(&'static str); +struct StructHolder(Struct2); +struct EnumHolder(Enum1); +struct ArrayHolder([u16; 2]); +struct TupleHolder((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) { + 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) = 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))); +} diff --git a/tests/ui/sanitizer/kcfi/const-generics.rs b/tests/ui/sanitizer/kcfi/const-generics.rs new file mode 100644 index 0000000000000..86f487bb9ea1e --- /dev/null +++ b/tests/ui/sanitizer/kcfi/const-generics.rs @@ -0,0 +1,109 @@ +// Verifies that functions with types with const generics as argument types can +// be called through function pointers. +// +//@ needs-sanitizer-kcfi +//@ only-linux +//@ ignore-backends: gcc +//@ compile-flags: -Ctarget-feature=-crt-static -Cpanic=abort -Cprefer-dynamic=off -Copt-level=0 -Zsanitizer=kcfi -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([i32; N]); +struct BoolHolder(bool); +struct IntHolder(i32); +struct CharHolder(char); +struct StrHolder(&'static str); +struct StructHolder(Struct2); +struct EnumHolder(Enum1); +struct ArrayHolder([u16; 2]); +struct TupleHolder((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) { + 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) = 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))); +}