-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tracking Issue for LLVM Control Flow Integrity (CFI) Support for Rust #89653
Comments
r? @eddyb |
This commit adds initial documentation for LLVM Control Flow Integrity (CFI) support to the Rust compiler (see rust-lang/rust#89652 and rust-lang/rust#89653).
This commit adds LLVM Control Flow Integrity (CFI) support to the Rust compiler. It initially provides forward-edge control flow protection for Rust-compiled code only by aggregating function pointers in groups identified by their number of arguments. Forward-edge control flow protection for C or C++ and Rust -compiled code "mixed binaries" (i.e., for when C or C++ and Rust -compiled code share the same virtual address space) will be provided in later work as part of this project by defining and using compatible type identifiers (see Type metadata in the design document in the tracking issue rust-lang#89653). LLVM CFI can be enabled with -Zsanitizer=cfi and requires LTO (i.e., -Clto).
Either work for me, I was just wondering about the current status. :)
Is there an issue tracking the availability on stable?
|
Not yet, but I'll create one right after. (FWIW, here is our roadmap for the rest of 2023 and beyond: https://hackmd.io/@rcvalle/H1epy5Xqn.) |
Is there a reason why unnormalized integers are supported in rustc at all? It is clearly not something that is supported by Rusts model and is something that is very C specific. I doubt there's a significant security benefit from not normalizing them, though I do not know details. Should that just be removed with only normalized integers being supported? |
@Nilstrieb what are unnormalized integers, which issue are you referring to? I read through the readme at https://crates.io/crates/cfi-types, and that part seems to be about the idiosyncrasies of C integer types. Is it really required to import those idiosyncrasies into every language that wants to have CFI interop with C? Even many C codebases stopped using |
@RalfJung These "idiosyncrasies", as you call them, are "unnormalized integers". The "normalized integers" are a CFI mapping using the integer types with their integer width, so in a C data model where The "unnormalized integers" violate that. |
Ah okay. I guess I am agreeing with Nilstrieb then, only normalized integers really make sense for Rust. |
Sorry for the late replies. I'm out traveling for speaking on a conference and then on vacation. Enabling the integer normalization option by default in rustc wouldn't help on cross-language CFI because most Rust integers have the same encoding as when normalized already (i.e., encoded as u<length><type-name> as vendor extended type), and most of the integer normalization work is actually done by the foreign language compiler (i.e., Clang). It'd just degrade the protection of a program written in Rust only (i.e., when a foreign language isn't used), and we want programs written in Rust only to have the best protection by default. The cfi_types crate helps in other scenarios, such as when a precompiled library needs to be linked to a program written in Rust, but wasn't compiled either with integer normalization or CFI enabled, and recompiling it isn't an option (and avoid adding e.g. https://github.com/rcvalle/rust/blob/55e3dc487fdf8025fb99301883e24af15323912a/library/std/src/sys/unix/thread_local_dtor.rs#L23 thoroughout the code). The integer normalization option also overrides the cfi_types creates use, so there are no incompatibility problems if a crate decides or needs to use it by default. |
How that? We explicitly document |
Are |
They are different types but ABI-compatible. |
Which means that for CFI they're different types, but can be used in place of one another if the proper conversion is done at the call site, and CFI will allow that. |
Could you give an example of that? Based on what @Nilstrieb says, we should at least clearly document than cross-language CFI is only expected to work with normalized integers. |
Your example demonstrates exactly what an attempt to redirect the control flow of a program by an attacker exploiting a (likely memory corruption) vulnerability would look like, and what CFI protects against, and why the integer normalization would degrage the security of programs written in Rust only (because it would allow it). An example that would work would be to have the correct function declaration/type for the function pointer and convert the type instead.
Yes, it's documented at https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity. |
Rust explicitly allows certain ABI mismatches. Those are not a sign of an attack. There's no chance of data misinterpretation or so here, behavior is entirely well-defined. If CFI rejects those programs then it rejects valid Rust programs. That's bad. Here's another example of a valid program.
Ah, great. :) |
What I'm saying is that these examples demonstrate it, not that these are cases that can and would be exploited. These demonstrate/have the same effect of an attacker redirecting the control flow of a program by exploiting a memory corruption vulnerability and overwriting a function pointer to perform a code reuse attack, redirecting the control flow to existing code they can benefit from. This is how type-based control flow protection works. It works at the language's type system level, and not the ABI level, and introduces such constraints (and in Rust case, these are unsafe operations/transmutes only, other languages are not), but provides ways to allow these with additional options, such as with the integer normalization and pointer generalization options. |
I think it's problematic to support "extra protection" by rejecting valid Rust programs. If CFI sees adoption in the ecosystem, people will start opening issues for crates that write correct code to get them to "fix their code" and support CFI. It can also be confusing to users, who start using CFI and then get crashes for their correct code. Documenting this as "If you want to use CFI on Rust, remember to pass this 'make it work' option" is also not ideal. Instead of having a 'make it work' option, it should just always work :). |
As I mentioned in another thread already, I wish fine-grained type-based CFI was an enable and forget mitigation as many others because I want as many users to use it as possible, but unfortunately it does require some code care and maintenance for use. (And it's not enabling integer normalization by default that is going to change that.) And It's actually common for large code bases to have those CFI violations when CFI support is initially implemented. For example, the Linux kernel had many that had to be fixed to have it initially supported and enabled. |
It is also fairly common for large C codebases to have UB. That does not apply to Rust though. In Rust we have a very clear baseline for what a program and a library is expected to ensure: no UB. Period. Having a "protection" mechanism apply higher standards than that is highly problematic and would need to be widely discussed in the project. I certainly think it's a bad idea. Protection mechanisms should detect UB. They shouldn't raise false positives in non-UB programs. Rust is not C, so not all things that make sense in C also make sense in Rust. |
Are you saying that codebases require some care to get working because they're filled with bugs that CFI exposes? If that's the case then that's great and we want that! I just see a significant difference between "it doesn't work because there's UB" and "it doesn't work because the sanitizer doesn't understand Rust semantics". The former is awesome and a feature, the latter is a bug that I'd like to see eliminated with integer normalization. |
Happy to continue this discussion in a new Zulip thread under project-exploit-mitigations. Let's move it there if you all agree. |
We should simply not support that. If clang wants to make arbitrary choices about how CFI works without considering cross-language compatibility, then we should simply refuse to be bullied by that, rather than treating it as dictating terms to us. We do not have the resources to play whack-a-mole games of chasing after language implementations that choose to implement recklessly inconsiderate semantics to interop. |
@rustbot label A-sanitizers |
This commit adds a new option (i.e., `-fsanitize-cfi-icall-normalize-integers`) for normalizing integer types as vendor extended types for cross-language LLVM CFI/KCFI support with other languages that can't represent and encode C/C++ integer types. Specifically, integer types are encoded as their defined representations (e.g., 8-bit signed integer, 16-bit signed integer, 32-bit signed integer, ...) for compatibility with languages that define explicitly-sized integer types (e.g., i8, i16, i32, ..., in Rust). ``-fsanitize-cfi-icall-normalize-integers`` is compatible with ``-fsanitize-cfi-icall-generalize-pointers``. This helps with providing cross-language CFI support with the Rust compiler and is an alternative solution for the issue described and alternatives proposed in the RFC rust-lang/rfcs#3296. For more information about LLVM CFI/KCFI and cross-language LLVM CFI/KCFI support for the Rust compiler, see the design document in the tracking issue rust-lang/rust#89653. Reviewed By: pcc, samitolvanen Differential Revision: https://reviews.llvm.org/D139395
This commit adds a new option (i.e., `-fsanitize-cfi-icall-normalize-integers`) for normalizing integer types as vendor extended types for cross-language LLVM CFI/KCFI support with other languages that can't represent and encode C/C++ integer types. Specifically, integer types are encoded as their defined representations (e.g., 8-bit signed integer, 16-bit signed integer, 32-bit signed integer, ...) for compatibility with languages that define explicitly-sized integer types (e.g., i8, i16, i32, ..., in Rust). ``-fsanitize-cfi-icall-normalize-integers`` is compatible with ``-fsanitize-cfi-icall-generalize-pointers``. This helps with providing cross-language CFI support with the Rust compiler and is an alternative solution for the issue described and alternatives proposed in the RFC rust-lang/rfcs#3296. For more information about LLVM CFI/KCFI and cross-language LLVM CFI/KCFI support for the Rust compiler, see the design document in the tracking issue rust-lang/rust#89653. Relands b1e9ab7 with fixes. Reviewed By: pcc, samitolvanen Differential Revision: https://reviews.llvm.org/D139395
This commit adds a new option (i.e., `-fsanitize-cfi-icall-normalize-integers`) for normalizing integer types as vendor extended types for cross-language LLVM CFI/KCFI support with other languages that can't represent and encode C/C++ integer types. Specifically, integer types are encoded as their defined representations (e.g., 8-bit signed integer, 16-bit signed integer, 32-bit signed integer, ...) for compatibility with languages that define explicitly-sized integer types (e.g., i8, i16, i32, ..., in Rust). ``-fsanitize-cfi-icall-normalize-integers`` is compatible with ``-fsanitize-cfi-icall-generalize-pointers``. This helps with providing cross-language CFI support with the Rust compiler and is an alternative solution for the issue described and alternatives proposed in the RFC rust-lang/rfcs#3296. For more information about LLVM CFI/KCFI and cross-language LLVM CFI/KCFI support for the Rust compiler, see the design document in the tracking issue rust-lang/rust#89653. Reviewed By: pcc, samitolvanen Differential Revision: https://reviews.llvm.org/D139395
This commit adds a new option (i.e., `-fsanitize-cfi-icall-normalize-integers`) for normalizing integer types as vendor extended types for cross-language LLVM CFI/KCFI support with other languages that can't represent and encode C/C++ integer types. Specifically, integer types are encoded as their defined representations (e.g., 8-bit signed integer, 16-bit signed integer, 32-bit signed integer, ...) for compatibility with languages that define explicitly-sized integer types (e.g., i8, i16, i32, ..., in Rust). ``-fsanitize-cfi-icall-normalize-integers`` is compatible with ``-fsanitize-cfi-icall-generalize-pointers``. This helps with providing cross-language CFI support with the Rust compiler and is an alternative solution for the issue described and alternatives proposed in the RFC rust-lang/rfcs#3296. For more information about LLVM CFI/KCFI and cross-language LLVM CFI/KCFI support for the Rust compiler, see the design document in the tracking issue rust-lang/rust#89653. Relands b1e9ab7 with fixes. Reviewed By: pcc, samitolvanen Differential Revision: https://reviews.llvm.org/D139395
This is a tracking issue for the LLVM Control Flow Integrity (CFI) Support for Rust project (see the design document).
Steps
LLVM CFI support for Rust work will be implemented in these steps:
Unresolved Questions
Implementation history
Bonus implementation
Known issues
https://github.com/rust-lang/rust/issues?q=is:open+label:PG-exploit-mitigations+-label:C-tracking-issue+CFI
The text was updated successfully, but these errors were encountered: