cfi_types crate
CFI types for cross-language LLVM CFI support.
Installation
To install the cfi_types crate:
-
Add the
cfi_types
crate to your package root'sCargo.toml
file:[dependencies] cfi-types = "0.0.1"
-
On a command prompt or terminal with your package root's directory as the current working directory, run the following command:
cargo fetch
Usage
To use the cfi_types crate:
-
Import the CFI types from the
cfi_types
crate. E.g.:use cfi_types::c_long;
-
Replace uses of C type aliases by CFI types. E.g.:
extern "C" { fn func(arg: c_long); } fn main() { unsafe { func(c_long(5)) }; }
Background
Type metadata
LLVM uses type metadata to allow IR modules to aggregate pointers by their types. 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 virtual tables and RTTI typeinfo structure name 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 (see Type metadata in the design document).
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 unable to identify if an
extern "C"
Fig. 1. Example extern "C" function using Rust integer type.
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, when types are encoded, all type aliases are
already resolved to their respective ty::Ty
type representations[15] (i.e.,
their respective Rust aliased types) making it currently impossible to identify
C type aliases use from their resolved types.
For example, the Rust compiler currently is also unable to identify that an
extern "C"
Fig. 2. Example extern "C" function using C type alias.
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 when
types are encoded.
Consequently, the Rust compiler is unable to identify and correctly encode C
types in extern "C"
function types indirectly called across the FFI boundary
when CFI is enabled.
// This definition has the type id "_ZTSFvlE".
void
// This definition has the type id "_ZTSFvPFvlElE"--this can be ignored for the
// purposes of this example.
void
Fig. 3. Example C library using C integer types and Clang encoding.
use c_long;
extern "C"
// This definition would have the type id "_ZTSFvlE", but is encoded either as
// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration
// above.
unsafe extern "C"
// This definition would have the type id "_ZTSFvlE", but is encoded either as
// "_ZTSFvu3i32E" or "_ZTSFvu3i64E", similarly to the hello_from_c declaration
// above.
unsafe extern "C"
// This definition would also have the type id "_ZTSFvPFvlElE", but is encoded
// either as "_ZTSFvPFvu3i32ES_E" (compressed) or "_ZTSFvPFvu3i64ES_E"
// (compressed), similarly to the hello_from_c declaration above--this can be
// ignored for the purposes of this example.
// This definition has the type id "_ZTSFvvE"--this can be ignored for the
// purposes of this example.
Fig. 4. Example Rust program using Rust integer types and the Rust compiler encoding.
Whenever there is an indirect call across the FFI boundary or an indirect call to a function passed as a callback across the FFI boundary, the Rust compiler and Clang use different encodings for C integer types for function definitions and declarations and at the indirect call sites when CFI is enabled (see Figs. 3–4).
The cfi_types crate
To solve the encoding C integer types problem, this crate provides a new set of
C types as user-defined types using the cfi_encoding
attribute and
repr(transparent)
to be used for cross-language LLVM CFI support.
use c_long;
extern "C"
// This definition has the type id "_ZTSFvlE" because it uses the CFI types for
// cross-language LLVM CFI support, similarly to the hello_from_c declaration
// above.
unsafe extern "C"
// This definition has the type id "_ZTSFvlE" because it uses the CFI types for
// cross-language LLVM CFI support, similarly to the hello_from_c declaration
// above.
unsafe extern "C"
// This definition would also have the type id "_ZTSFvPFvlElE" because it uses
// the CFI types for cross-language LLVM CFI support, similarly to the
// hello_from_c declaration above--this can be ignored for the purposes of this
// example.
// This definition has the type id "_ZTSFvvE"--this can be ignored for the
// purposes of this example.
Fig. 5. Example Rust program using Rust integer types and the Rust compiler encoding with the cfi_types crate types.
This new set of C types allows the Rust compiler to identify and correctly encode C types in extern "C" function types indirectly called across the FFI boundary when CFI is enabled (see Fig 5).
Contributing
See CONTRIBUTING.md.
License
Licensed under the Apache License, Version 2.0 or the MIT License. See LICENSE-APACHE or LICENSE-MIT for license text and copyright information.