Skip to content

Commit

Permalink
CFI: Fix ICE: encode_const: unexpected type [usize
Browse files Browse the repository at this point in the history
Fixes #100778 and #113366, and complements #106547 by adding support for
encoding const parameters.
  • Loading branch information
rcvalle committed Jul 17, 2023
1 parent b3c7a7e commit 55dea62
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 34 deletions.
79 changes: 45 additions & 34 deletions compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
///
/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
/// see design document in the tracking issue #89653.
use core::fmt::Display;
use rustc_data_structures::base_n;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{
self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind,
TermKind, Ty, TyCtxt, UintTy,
Expand All @@ -19,6 +19,7 @@ use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
use rustc_span::def_id::DefId;
use rustc_span::sym;
use rustc_target::abi::call::{Conv, FnAbi};
use rustc_target::abi::Integer;
use rustc_target::spec::abi::Abi;
use std::fmt::Write as _;

Expand Down Expand Up @@ -93,44 +94,54 @@ fn encode_const<'tcx>(
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
options: EncodeTyOptions,
) -> String {
// L<element-type>[n]<element-value>E as literal argument
// L<element-type>[n][<element-value>]E as literal argument
let mut s = String::from('L');

// Element type
s.push_str(&encode_ty(tcx, c.ty(), dict, options));
match c.kind() {
// Const parameters
ty::ConstKind::Param(..) => {
// L<element-type>E as literal argument

// The only allowed types of const parameters 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.
fn push_signed_value<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) {
if value < zero {
s.push('n')
};
let _ = write!(s, "{value}");
}

fn push_unsigned_value<T: Display>(s: &mut String, value: T) {
let _ = write!(s, "{value}");
}
// Element type
s.push_str(&encode_ty(tcx, c.ty(), dict, options));
}

if let Some(scalar_int) = c.try_to_scalar_int() {
let signed = c.ty().is_signed();
match scalar_int.size().bits() {
8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0),
16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0),
32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0),
64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0),
128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0),
8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()),
16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()),
32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()),
64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()),
128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()),
_ => {
bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits());
// Literal arguments
ty::ConstKind::Value(..) => {
// L<element-type>[n]<element-value>E as literal argument

// Element type
s.push_str(&encode_ty(tcx, c.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 c.ty().kind() {
ty::Int(ity) => {
let bits = c.eval_bits(tcx, ty::ParamEnv::reveal_all(), c.ty());
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 = c.eval_bits(tcx, ty::ParamEnv::reveal_all(), c.ty());
let _ = write!(s, "{val}");
}
ty::Bool => {
let val = c.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap();
let _ = write!(s, "{val}");
}
_ => {
bug!("encode_const: unexpected type `{:?}`", c.ty());
}
}
};
} else {
bug!("encode_const: unexpected type `{:?}`", c.ty());
}

_ => {
bug!("encode_const: unexpected kind `{:?}`", c.kind());
}
}

// Close the "L..E" pair
Expand Down
29 changes: 29 additions & 0 deletions tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ impl<'a, T, U> Trait4<'a, U> for T {
}
}

pub trait Trait5<T, const N: usize> {
fn quux(&self, _: &[T; N]);
}

#[derive(Copy, Clone)]
pub struct Type5;

impl<T, U, const N: usize> Trait5<U, N> for T {
fn quux(&self, _: &[U; N]) {
}
}

pub fn foo1(a: &dyn Trait1) {
a.foo();
// CHECK-LABEL: define{{.*}}4foo1{{.*}}!type !{{[0-9]+}}
Expand Down Expand Up @@ -114,7 +126,24 @@ pub fn bar4<'a>() {
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE4:[[:print:]]+]]")
}

pub fn foo5(a: &dyn Trait5<Type5, 32>) {
let b = &[Type5; 32];
a.quux(&b);
// CHECK-LABEL: define{{.*}}4foo5{{.*}}!type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE5:[[:print:]]+]]")
}

pub fn bar5() {
let a = &[Type5; 32];
foo5(&a);
let b = &a as &dyn Trait5<Type5, 32>;
b.quux(&a);
// CHECK-LABEL: define{{.*}}4bar5{{.*}}!type !{{[0-9]+}}
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE5:[[:print:]]+]]")
}

// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE1]]"}
// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE2]]"}
// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE3]]"}
// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE4]]"}
// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE5]]"}
30 changes: 30 additions & 0 deletions tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ impl<'a, T, U> Trait4<'a, U> for T {
}
}

pub trait Trait5<T, const N: usize> {
fn quux(&self, _: &[T; N]);
}

pub struct Type5;

impl Copy for Type5 {}

impl<T, U, const N: usize> Trait5<U, N> for T {
fn quux(&self, _: &[U; N]) {
}
}

pub fn foo1(a: &dyn Trait1) {
a.foo();
// CHECK-LABEL: define{{.*}}4foo1{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
Expand Down Expand Up @@ -138,7 +151,24 @@ pub fn bar4<'a>() {
// CHECK: call align 4 {{ptr|i32\*}} %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}, {{\{\}\*|ptr|%Type4\*}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE4:[[:print:]]+]]) ]
}

pub fn foo5(a: &dyn Trait5<Type5, 32>) {
let b = &[Type5; 32];
a.quux(&b);
// CHECK-LABEL: define{{.*}}4foo5{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z](\.0)*|%_[0-9]+]}}, {{\{\}\*|ptr|\[32 x %Type5\]\*}} align 1 {{%[a-z](\.0)*|%_[0-9]+}}){{.*}}[ "kcfi"(i32 [[TYPE5:[[:print:]]+]]) ]
}

pub fn bar5() {
let a = &[Type5; 32];
foo5(&a);
let b = &a as &dyn Trait5<Type5, 32>;
b.quux(&a);
// CHECK-LABEL: define{{.*}}4bar5{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
// CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z](\.0)*|%_[0-9]+]}}, {{\{\}\*|ptr|\[32 x %Type5\]\*}} align 1 {{%[a-z](\.0)*|%_[0-9]+}}){{.*}}[ "kcfi"(i32 [[TYPE5:[[:print:]]+]]) ]
}

// CHECK: !{{[0-9]+}} = !{i32 [[TYPE1]]}
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE2]]}
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE3]]}
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE4]]}
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE5]]}

0 comments on commit 55dea62

Please sign in to comment.