---
# System prepended metadata

title: Summary

---

- Feature Name: `improve-c-types-for-cross-language-cfi`
- Start Date: 2022-07-25
- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

# Summary
[summary]: #summary

Improve C types for cross-language LLVM CFI support.

# Motivation
[motivation]: #motivation

This RFC is part of the LLVM Control Flow Integrity (CFI) Support for Rust, and
is a requirement for cross-language LLVM CFI support.

For cross-language LLVM CFI support, the Rust compiler must be able to identify
and correctly encode C types in extern "C" function types indirectly called
(i.e., function pointers) across the FFI boundary when cross-language CFI
support is needed.

For convenience, Rust provides some C-like type aliases for use when
interoperating with foreign code written in C, and these C type aliases may be
used for identification. However, at the time types are encoded, all type
aliases are already resolved to their respective Rust aliased types, making it
currently not possible to identify C type aliases use from their resolved types.

For example, the Rust compiler currently is not able to identify that an

```rust
extern "C" {
    fn func(arg: c_long);
}
```

used the `c_long` type alias and is not able to disambiguate between it and an
`extern "C" fn func(arg: c_longlong)` in an LP64 or equivalent data model at the
time types are encoded.

This motivates creating a new set of C types that their use can be identified at
the time types are encoded to be used in extern "C" function types indirectly
called across the FFI boundary when cross-language CFI support is needed.

For more information about and the motivation for the project, see the design
document in the tracking issue [#89653][1][[1]] and the [Appendix][appendix].

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

This RFC proposes creating a new set of C types in `core::ffi::cfi` as
user-defined types using `repr(transparent)` to be used in extern "C" function
types indirectly called across the FFI boundary when cross-language CFI support
is needed, and keeping the existing C-like type aliases.

The new set of C types will make indirect calls to extern "C" function types
across the FFI boundary work when CFI is enabled. These indirect calls will
continue to not work when CFI is enabled unless the new set of C types are used.

These are not backward-compatibility breaking changes because the Rust compiler
currently does not support cross-language CFI (i.e., extern "C" function types
indirectly called across the FFI boundary when CFI is enabled).

For example:

example/src/main.rs
```rust
use std::ffi::c_long;

#[link(name = "foo")]
extern "C" {
    fn hello_from_c(_: c_long);
    fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long);
}

unsafe extern "C" fn hello_from_rust(_: c_long) {
    println!("Hello, world!");
}

unsafe extern "C" fn hello_from_rust_again(_: c_long) {
    println!("Hello from Rust again!\n");
}

fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) {
    unsafe { f(arg) }
}

fn main() {
    // This works
    indirect_call(hello_from_rust, 1);
    // This works when using rustc LTO, but does not work when using (proper)
    // LTO because the Rust compiler and Clang use different encodings for
    // hello_from_c and the test at the indirect call site at indirect_call.
    indirect_call(hello_from_c, 2);
    // This does not work because the Rust compiler and Clang use different
    // encodings for hello_from_rust_again and the test at the indirect call
    // site at indirect_call_from_c.
    unsafe {
        indirect_call_from_c(hello_from_rust_again, 3);
    }
}
```

example/src/foo.c
```c
#include <stdio.h>
#include <stdlib.h>

void
hello_from_c(long arg)
{
    printf("Hello from C!\n");
}

void
indirect_call_from_c(void (*fn)(long), long arg)
{
    fn(arg);
}
```

Will need to be changed to:

example/src/main.rs
```rust
use std::ffi::c_long;
use std::ffi::cfi;

#[link(name = "foo")]
extern "C" {
    fn hello_from_c(_: cfi::c_long);
    fn indirect_call_from_c(f: unsafe extern "C" fn(cfi::c_long), arg: c_long);
}

unsafe extern "C" fn hello_from_rust(_: cfi::c_long) {
    println!("Hello, world!");
}

unsafe extern "C" fn hello_from_rust_again(_: cfi::c_long) {
    println!("Hello from Rust again!\n");
}

fn indirect_call(f: unsafe extern "C" fn(cfi::c_long), arg: c_long) {
    unsafe { f(cfi::c_long(arg)) }
}

fn main() {
    // This will continue to work
    indirect_call(hello_from_rust, 1);
    // This will work both when using rustc LTO and when using (proper) LTO
    // because the Rust compiler and Clang will use the same encoding for
    // hello_from_c and the test at the indirect call site at indirect_call.
    indirect_call(hello_from_c, 2);
    // This will work because the Rust compiler and Clang will use the same
    // encoding for hello_from_rust_again and the test at the indirect call site
    // at indirect_call_from_c.
    unsafe {
        indirect_call_from_c(hello_from_rust_again, 3);
    }
}
```

example/src/foo.c
```c
#include <stdio.h>
#include <stdlib.h>

void
hello_from_c(long arg)
{
    printf("Hello from C!\n");
}

void
indirect_call_from_c(void (*fn)(long), long arg)
{
    fn(arg);
}
```

Direct calls to extern "C" function types across the FFI boundary, whether CFI
is enabled or disabled, will continue to work whether Rust integer types or C
type aliases are used.

For example:

example/src/main.rs
```rust
// Optionally, use std::ffi::c_long. (Note this is the C type alias, not
// the new C type.)

#[link(name = "foo")]
extern "C" {
    fn hello_from_c(_: i64);
    // Or fn hello_from_c(_: c_long). (Note this is the C type alias,
    // not the new C type.)
}

fn main() {
    unsafe { hello_from_c(1); }
}
```

example/src/foo.c
```c
#include <stdio.h>
#include <stdlib.h>

void
hello_from_c(long arg)
{
    printf("Hello from C!\n");
}
```

Will continue to work when `fn hello_from_c(_: i64)` or `fn hello_from_c(_:
c_long)` represents a `void hello_from_c(long arg)` in an LP64 or equivalent
data model.

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

## Type metadata
[type-metadata]: #type-metadata

LLVM uses type metadata to allow IR modules to aggregate pointers by their
types.[[2]] This type metadata is used by LLVM Control Flow Integrity to test
whether a given pointer is associated with a type identifier (i.e., test type
membership).

Clang uses the Itanium C++ ABI's[[3]] virtual tables and RTTI typeinfo structure
name[[4]] as type metadata identifiers for function pointers.

For cross-language LLVM CFI support, a compatible encoding must be used. The
compatible encoding chosen for cross-language LLVM CFI support is the Itanium
C++ ABI mangling with vendor extended type qualifiers and types for Rust types
that are not used across the FFI boundary.

## Encoding C integer types

Rust defines `char` as an Unicode scalar value, while C defines `char` as an
integer type. Rust also defines explicitly-sized integer types (i.e., `i8`,
`i16`, `i32`, ...) while C defines abstract integer types (i.e., `char`,
`short`, `long`, ...), which actual sizes are implementation defined and may
vary across different data models. This causes ambiguity if Rust integer types
are used in extern "C" function types that represent C functions because the
Itanium C++ ABI specifies encodings for C integer types (e.g., `char`, `short`,
`long`, ...), not their defined representations (e.g., 8-bit signed integer,
16-bit signed integer, 32-bit signed integer, ...).

For example, the Rust compiler currently is not able to identify if an

```rust
extern "C" {
    fn func(arg: i64);
}
```

represents a `void func(long arg)` or `void func(long long arg)` in an LP64 or
equivalent data model.

For cross-language LLVM CFI support, the Rust compiler must be able to identify
and correctly encode C types in extern "C" function types indirectly called
across the FFI boundary when CFI is enabled.

For convenience, Rust provides some C-like type aliases for use when
interoperating with foreign code written in C, and these C type aliases may be
used for disambiguation. However, at the time types are encoded, all type
aliases are already resolved to their respective `ty::Ty` type
representations[[5]] (i.e., their respective Rust aliased types) making it
currently not possible to identify C type aliases use from their resolved types.

For example, the Rust compiler currently is also not able to identify that an

```rust
extern "C" {
    fn func(arg: c_long);
}
```

used the `c_long` type alias and is not able to disambiguate between it and an
`extern "C" fn func(arg: c_longlong)` in an LP64 or equivalent data model at the
time types are encoded.

This RFC proposes creating a new set of C types in `core::ffi::cfi` as
user-defined types using `repr(transparent)` to be used in extern "C" function
types indirectly called across the FFI boundary when cross-language CFI support
is needed, and keeping the existing C-like type aliases.

The new set of C types will make indirect calls to extern "C" function types
across the FFI boundary work when CFI is enabled. These indirect calls will
continue to not work when CFI is enabled unless the new set of C types are used.

These are not backward-compatibility breaking changes because the Rust compiler
currently does not support cross-language CFI (i.e., extern "C" function types
indirectly called across the FFI boundary when CFI is enabled).

For example:

example/src/main.rs
```rust
use std::ffi::c_long;

#[link(name = "foo")]
extern "C" {
    // This declaration would have the type id "_ZTSFvlE", but at the time types
    // are encoded, all type aliases are already resolved to their respective
    // Rust aliased types, so this is encoded as either "_ZTSFvu3i32E" or
    // "_ZTSFvu3i64E", depending to what type c_long type alias is resolved to,
    // which currently uses the u<length><type-name> vendor extended type
    // encoding for the Rust integer types--this is the issue this RFC
    // describes.
    fn hello_from_c(_: c_long);

    // This declaration would have the type id "_ZTSFvPFvlElE", but is encoded
    // as either "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E"
    // (compressed), similarly to the hello_from_c declaration above--this may
    // be ignored for the purposes of this example.
    fn indirect_call_from_c(f: unsafe extern "C" fn(c_long), arg: c_long);
}

// This definition would have the type id "_ZTSFvlE", but is encoded as either
// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration
// above.
unsafe extern "C" fn hello_from_rust(_: c_long) {
    println!("Hello, world!");
}

// This definition would have the type id "_ZTSFvlE", but is encoded as either
// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration
// above.
unsafe extern "C" fn hello_from_rust_again(_: c_long) {
    println!("Hello from Rust again!\n");
}

// This definition would also have the type id "_ZTSFvPFvlElE", but is encoded
// as either "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E"
// (compressed), similarly to the hello_from_c declaration above--this may be
// ignored for the purposes of this example.
fn indirect_call(f: unsafe extern "C" fn(c_long), arg: c_long) {
    // This indirect call site tests whether the destinatin pointer is a member
    // of the group derived from the same type id of the f declaration, which
    // would have the type id "_ZTSFvlE", but is encoded as either
    // "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c
    // declaration above.
    //
    // Notice that since the test is at the call site and generated by the Rust
    // compiler, the type id used in the test is encoded by the Rust compiler.
    unsafe { f(arg) }
}

// This definition has the type id "_ZTSFvvE"--this may be ignored for the
// purposes of this example.
fn main() {
    // This demonstrates an indirect call within Rust-only code using the same
    // encoding for hello_from_rust and the test at the indirect call site at
    // indirect_call (i.e., "_ZTSFvu3i32E" or "_ZTSFvu3i64E").
    indirect_call(hello_from_rust, 1);

    // This demonstrates an indirect call across the FFI boundary with the Rust
    // compiler and Clang using different encodings for hello_from_c and the
    // test at the indirect call site at indirect_call (i.e., "_ZTSFvu3i32E" or
    // "_ZTSFvu3i64E" vs "_ZTSFvlE").
    //
    // When using rustc LTO (i.e., make using_rustc_lto), this works because the
    // declaration used is the Rust-declared hello_from_c, which has the type id
    // encoded by the Rust compiler (i.e., "_ZTSFvu3i32E" or "_ZTSFvu3i64E").
    //
    // When using (proper) LTO (i.e., make), this does not work because the
    // declaration used is the C-defined hello_from_c, which has the type id
    // encoded by Clang (i.e., "_ZTSFvlE").
    indirect_call(hello_from_c, 2);

    // This demonstrates an indirect call to a function passed as a callback
    // across the FFI boundary with the Rust compiler and Clang using different
    // encodings for the passed-callback declaration and the test at the
    // indirect call site at indirect_call_from_c (i.e., "_ZTSFvu3i32E" or
    // "_ZTSFvu3i64E" vs "_ZTSFvlE").
    //
    // When Rust functions are passed as callbacks across the FFI boundary to be
    // called back from C code, the tests are also at the call site but
    // generated by Clang instead, so the type ids used in the tests are encoded
    // by Clang, which will currently not match the type ids of declarations
    // encoded by the Rust compiler (e.g., hello_from_rust_again). (The same
    // happens the other way around for C funtions passed as callbacks across
    // the FFI boundary to be called back from Rust code.)
    unsafe {
        indirect_call_from_c(hello_from_rust_again, 3);
    }
}
```

example/src/foo.c
```c
#include <stdio.h>
#include <stdlib.h>

// This definition has the type id "_ZTSFvlE".
void
hello_from_c(long arg)
{
    printf("Hello from C!\n");
}

// This definition has the type id "_ZTSFvPFvlElE"--this may be ignored for the
// purposes of this example.
void
indirect_call_from_c(void (*fn)(long), long arg)
{
    // This call site tests whether the destinatin pointer is a member of the
    // group derived from the same type id of the fn declaration, which has the
    // type id "_ZTSFvlE".
    //
    // Notice that since the test is at the call site and generated by Clang,
    // the type id used in the test is encoded by Clang.
    fn(arg);
}
```

Will need to be changed to:

example/src/main.rs
```rust
use std::ffi::c_long;
use std::ffi::cfi;

// The new set of C types in `core::ffi::cfi` as user-defined types using
// `repr(transparent)` will be equivalent to (using c_long as an example):
//
// pub mod cfi {
//     #[allow(non_camel_case_types)]
//     #[repr(transparent)]
//     pub struct c_long(pub std::ffi::c_long);
// }

#[link(name = "foo")]
extern "C" {
    // This declaration will have the type id "_ZTSFvlE".
    fn hello_from_c(_: cfi::c_long);

    // This declaration will have either the type id "_ZTSFvPFvlEu3i32E" or
    // "_ZTSFvPFvlEu3i64E"--this may be ignored for the purposes of this
    // example.
    fn indirect_call_from_c(f: unsafe extern "C" fn(cfi::c_long), arg: c_long);
}

// This definition will have the type id "_ZTSFvlE".
unsafe extern "C" fn hello_from_rust(_: cfi::c_long) {
    println!("Hello, world!");
}

// This definition will have the type id "_ZTSFvlE".
unsafe extern "C" fn hello_from_rust_again(_: cfi::c_long) {
    println!("Hello from Rust again!\n");
}

// This definition will also have either the type id "_ZTSFvPFvlEu3i32E" or
// "_ZTSFvPFvlEu3i64E"--this may be ignored for the purposes of this example.
fn indirect_call(f: unsafe extern "C" fn(cfi::c_long), arg: c_long) {
    // This indirect call site tests whether the destinatin pointer is a member
    // of the group derived from the same type id of the f declaration, which
    // will have the type id "_ZTSFvlE".
    //
    // Notice that since the test is at the call site and generated by the Rust
    // compiler, the type id used in the test is encoded by the Rust compiler.
    unsafe { f(cfi::c_long(arg)) }
}

// This definition has the type id "_ZTSFvvE"--this may be ignored for the
// purposes of this example.
fn main() {
    // This demonstrates an indirect call within Rust-only code using the same
    // encoding for hello_from_rust and the test at the indirect call site at
    // indirect_call (i.e., "_ZTSFvlE").
    indirect_call(hello_from_rust, 1);

    // This demonstrates an indirect call across the FFI boundary with the Rust
    // compiler and Clang using the same encoding for hello_from_c and the test
    // at the indirect call site at indirect_call (i.e., "_ZTSFvlE").
    indirect_call(hello_from_c, 2);

    // This demonstrates an indirect call to a function passed as a callback
    // across the FFI boundary with the Rust compiler and Clang using the same
    // encoding for the passed-callback declaration and the test at the indirect
    // call site at indirect_call_from_c (i.e., "_ZTSFvlE").
    unsafe {
        indirect_call_from_c(hello_from_rust_again, 3);
    }
}
```

example/src/foo.c
```c
#include <stdio.h>
#include <stdlib.h>

// This definition has the type id "_ZTSFvlE".
void
hello_from_c(long arg)
{
    printf("Hello from C!\n");
}

// This definition has the type id "_ZTSFvPFvlElE"--this may be ignored for the
// purposes of this example.
void
indirect_call_from_c(void (*fn)(long), long arg)
{
    // This call site tests whether the destinatin pointer is a member of the
    // group derived from the same type id of the fn declaration, which has the
    // type id "_ZTSFvlE".
    //
    // Notice that since the test is at the call site and generated by Clang,
    // the type id used in the test is encoded by Clang.
    fn(arg);
}
```

Direct calls to extern "C" function types across the FFI boundary, whether CFI
is enabled or disabled, will continue to work whether Rust integer types or C
type aliases are used.

For example:

example/src/main.rs
```rust
// Optionally, use std::ffi::c_long. (Note this is the C type alias, not
// the new C type.)

#[link(name = "foo")]
extern "C" {
    // This declaration will have the type id "_ZTSFvu3i64E".
    fn hello_from_c(_: i64);
    // This declaration will have either the type id "_ZTSFvu3i32E" or
    // "_ZTSFvu3i64E".
    // Or fn hello_from_c(_: c_long). (Note this is the C type alias,
    // not the new C type.)
}

// This definition has the type id "_ZTSFvvE"--this may be ignored for the
// purposes of this example.
fn main() {
    // This will continue to work because direct call sites do not test type
    // membership.
    unsafe { hello_from_c(1); }
}
```

example/src/foo.c
```c
#include <stdio.h>
#include <stdlib.h>

// This definition has the type id "_ZTSFvlE".
void
hello_from_c(long arg)
{
    printf("Hello from C!\n");
}
```

Will continue to work when `fn hello_from_c(_: i64)` or `fn hello_from_c(_:
c_long)` represents a `void hello_from_c(long arg)` in an LP64 or equivalent
data model.

# Drawbacks
[drawbacks]: #drawbacks

The Rust compiler assumes that C char and integer types and their respective
Rust aliased types can be used interchangeably. These assumptions can not be
maintained for extern "C" function types indirectly called across the FFI
boundary when CFI is enabled and the new set of C types are used.

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

The alternatives considered were:

  1. creating a new set of C types in `core::ffi::cfi` as user-defined types
     using `repr(transparent)` to be used in extern "C" function types
     indirectly called across the FFI boundary when cross-language CFI support
     is needed, and keeping the existing C-like type aliases.

  2. waiting for the work in progress in rust-lang/rust#97974 for
     rust-lang/compiler-team#504 and use type alias information for
     disambiguation and to specify the corresponding C types in extern "C"
     function types when cross-language CFI support is needed.

  3. adding a new set of parameter attributes to specify the corresponding C
     types to be used in extern "C" function types indirectly called across the
     FFI boundary when cross-language CFI support is needed.

  4. creating a new set of transitional C types in `core::ffi` as user-defined
     types using `repr(transparent)` to be used in extern "C" function types
     indirectly called across the FFI boundary when cross-language CFI support
     is needed (and taking the opportunity to consolidate all C types in
     `core::ffi`).

  5. changing the currently existing C types in `std::os::raw` to user-defined
     types using `repr(transparent)`.

  6. changing C types to `ty::Foreign` and changing `ty::Foreign` to be able to
     represent them.

  7. creating a new `ty::C` for representing C types.

Alternatives (1), (2), and (3) are opt in for when cross-language CFI support is
needed. These alternatives are not backward-compatibility breaking changes
because the Rust compiler currently does not support cross-language CFI (i.e.,
extern "C" function types indirectly called across the FFI boundary when CFI is
enabled).

Alternatives (4), (5), (6), and (7) are backward-compatibility breaking changes
because they will require changes to existing code that use C types.

The solution this RFC proposes (1) is opt in, is not a backward-compatibility
breaking change, and is one of the less intrusive changes to the language among
the alternatives listed.

# Prior art
[prior-art]: #prior-art

The author is currently not aware of any cross-language CFI implementation and
support by any other compiler and language.

# Unresolved questions
[unresolved-questions]: #unresolved-questions

None.

# Future possibilities
[future-possibilities]: #future-possibilities

The project this RFC is part of and solving the issue this RFC describes
provides the foundation for cross-language CFI support for the Linux kernel
(i.e., cross-language kCFI support) and Intel Fine Indirect Branch Tracking
(FineIBT), which use the same encoding and also depend on solving the issue this
RFC describes.

It also provides the foundation for future implementations of cross-language
hardware-assisted and software-based -combined forward-edge control flow
protection, such as Microsoft Windows eXtended Flow Guard (XFG) and ARM Pointer
Authentication -based forward-edge control flow protection, that also depend on
the Rust compiler being able to identify C char and integer type uses at the
time types are encoded.

# Acknowledgment

Thanks to pnkfelix (Felix Klock) and the Rust community for all their help on
this RFC.

# Appendix
[appendix]: #appendix

As the industry continues to explore Rust adoption, the absence of support for
forward-edge control flow protection in the Rust compiler is a major security
concern when migrating to Rust by gradually replacing C or C++ with Rust, and C
or C++ and Rust -compiled code share the same virtual address space.

A safe language -compiled code such as Rust, when sharing the same virtual
address space with an unsafe language -compiled code such as C or C++, may
degrade the security of a program because of different assumptions about
language properties and availability of security features such as exploit
mitigations.

The issue the project this RFC is part of aims to solve is an example of this,
where entirely safe Rust-compiled code, when sharing the same virtual address
space with C or C++ -compiled code with forward-edge control flow protection,
may degrade the security of the program because the indirect branches in
Rust-compiled code are not validated, allowing forward-edge control flow
protection to be trivially bypassed.

This has been extensively discussed[[6]][[7]][[8]][[9]][[10]], and just recently
formalized[[11]] as a new class of attack (i.e., cross-language attacks). It was
also one of the major reasons that initiatives such as Rust GCC--which this
author also fully support--were funded[[10]]. Therefore, support for
forward-edge control flow protection needs to be added to the Rust compiler and
is a requirement for large-scale secure Rust adoption.

# Frequently asked questions (FAQ)
[faq]: #faq

## Are the changes proposed in this RFC backward-compatibility breaking changes?

These are not backward-compatibility breaking changes because the Rust compiler
currently does not support cross-language CFI (i.e., extern "C" function types
indirectly called across the FFI boundary when CFI is enabled).

## Why not use the v0 mangling scheme for encoding?

The v0 mandling scheme can not be used because it is not a compatible encoding
for cross-language LLVM CFI support.

## Why not create a new encoding for cross-language CFI?

See Using Itanium C++ ABI mangling for encoding (1) versus creating a new
encoding for cross-language CFI (2) in the design document in the tracking issue
[#89653][1][[1]].

## Why not use a generalized encoding across the FFI boundary?

This results in less comprehensive protection, may result in using a generalized
encoding for all C and C++ -compiled code instead of only across the FFI
boundary depending whether Clang can be changed to use the generalized encoding
only across the FFI boundary (which may also require new Clang extensions and
changes to C and C++ code and libraries), and will degrade the security of the
program when linking foreign Rust-compiled code into a program written in C or
C++ because the program previously used a more comprehensive encoding for all
its compiled code.

## Why not use hardware-assisted forward-edge control flow protection?

Newer processors provide hardware assistance for forward-edge control flow
protection, such as ARM Branch Target Identification (BTI), ARM Pointer
Authentication, and Intel Indirect Branch Tracking (IBT) as part of Intel
Control-flow Enforcement Technology (CET). However, ARM BTI and Intel IBT -based
implementations are less comprehensive than software-based implementations such
as [LLVM ControlFlowIntegrity
(CFI)](https://clang.llvm.org/docs/ControlFlowIntegrity.html), and the
commercially available [grsecurity/PaX Reuse Attack Protector
(RAP)](https://grsecurity.net/rap_faq).

## What do you mean by less comprehensive protection?

The less comprehensive the protection, the higher the likelihood it can be
bypassed. For example, Microsoft Windows Control Flow Guard (CFG) only tests
that the destination of an indirect branch is a valid function entry point,
which is the equivalent of grouping all function pointers in a single group, and
testing all destinations of indirect branches to be in this group. This is also
known as "coarse-grained CFI".

(This is even less comprehensive than the initial support for LLVM CFI added to
the Rust compiler as part of the project this RFC is also part of, which
aggregated function pointers in groups identified by their number of parameters
[i.e., rust-lang/rust#89652], and provides protection only for the first example
listed in the partial results in the design document in the tracking issue
[#89653][1][[1]])

It means that in an exploitation attempt, an attacker can change/hijack control
flow to any function, and the larger the program is, the higher the likelihood
an attacker can find a function they can benefit from (e.g., a small
command-line program vs a browser).

This is unfortunately the implementation hardware assistance (e.g., ARM BTI and
Intel IBT) were initially modeled based on for forward-edge control flow
protection, and as such they provide equivalent protection with the addition of
specialized instructions. Microsoft Windows eXtended Flow Guard (XFG), ARM
Pointer Authentication -based forward-edge control flow protection, and Intel
Fine Indirect Branch Tracking (FineIBT) aim to solve this by combining hardware
assistance with software-based function pointer type testing similarly to LLVM
CFI. This is also known as "fine-grained CFI".

(This is equivalent to the current support for LLVM CFI added to the Rust
compiler as part of the project this RFC is also part of, which aggregates
function pointers in groups identified by their return and parameter types
[i.e., rust-lang/rust#95548]. See the partial results in the design document in
the tracking issue [#89653][1][[1]].)

[1]: <https://github.com/rust-lang/rust/issues/89653> "R. de C Valle. \"Tracking Issue for LLVM Control Flow Integrity (CFI) Support for Rust #89653.\" GitHub."
[2]: <https://llvm.org/docs/TypeMetadata.html> "\"Type Metadata.\" LLVM Documentation."
[3]: <https://itanium-cxx-abi.github.io/cxx-abi/abi.html> "\"Itanium C++ ABI\"."
[4]: <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-special-vtables> "\"Virtual Tables and RTTI\". Itanium C++ ABI."
[5]: <https://rustc-dev-guide.rust-lang.org/ty.html> "\"The ty module: representing types\". Guide to Rustc Development."
[6]: https://stanford-cs242.github.io/f17/assets/projects/2017/songyang.pdf "Y. Song. \"On Control Flow Hijacks of unsafe Rust.\" GitHub."
[7]: https://www.cs.ucy.ac.cy/~elathan/papers/tops20.pdf "M. Papaevripides and E. Athanasopoulos. \"Exploiting Mixed Binaries.\" Elias Athanasopoulos Publications."
[8]: https://github.com/rust-lang/rust/files/4723836/Control.Flow.Guard.for.Rust.pdf "A. Paverd. \"Control Flow Guard for Rust.\" GitHub."
[9]: https://github.com/rust-lang/rust/files/4723840/Control.Flow.Guard.for.LLVM.pdf "A. Paverd. \"Control Flow Guard for LLVM.\" GitHub."
[10]: https://opensrcsec.com/open_source_security_announces_rust_gcc_funding "B. Spengler. \"Open Source Security, Inc. Announces Funding of GCC Front-End for Rust.\" Open Source Security."
[11]: https://www.ndss-symposium.org/wp-content/uploads/2022-78-paper.pdf "S. Mergendahl, N. Burow, H. Okhravi. \"Cross-Language Attacks.\" NDSS Symposium 2022."
