-
Notifications
You must be signed in to change notification settings - Fork 51
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
cfi: Simpler launder implementation for common types #1714
Conversation
1921e07
to
05a5902
Compare
(I've also looked at removing There are more likely a few more types though that would be worth streamlining so we can save more code space, but I leave that to a future PR.) |
@jhand2, @nquarton, @rusty1968 -- approvals were wiped when I rebased and updated the SHAs. No other changes were made. Please re-review and I'll get someone to merge this ASAP for the 1.2 ROM release. |
… value away. Our current implementation uses `core::hint::black_box()`, which is the recommended way in Rust. The problem is, this appears to often force the argument to spill into memory and to be reloaded, which can be a lot of extra instructions. The original inspiration for this function is from, I believe, [OpenTitan's launder* functions](https://github.com/lowRISC/opentitan/blob/master/sw/device/lib/base/hardened.h#L193). There, they use an LLVM-specific trick of a blank inline assembly block to force the compiler to keep the argument in a register. After reviewing our code and speaking with @vsonims, it sounds like the intention of the launder in our code is to prevent the compiler from optimizing the value away (as the comments suggest), so the simpler inline assembly trick may be sufficient (since we use the official Rust compiler, which uses LLVM). The biggest problem is that we launder many types of values in our code and not all of them fit into a register. So, this PR represents an incremental change: for `u32`s and similar small types, we implement `cfi_launder` using the inline assembly trick from OpenTitan. For any other types, we have a trait that can be derived that will call `core::hint::black_box` in the same way as today. We can do future follow-up PRs to try to try to clean up some of those other uses of `cfi_launder` to hopefully shrink the code more. I also slipped in avoid a few extra copies in the verifier by using references instead of copies (this saves ~80 bytes of instruction space). This PR appears to shrink the ROM code size by 1232 bytes and the runtime firmware by 700 bytes.
Thanks! |
* `cfi_launder` is meant to prevent the Rust compiler from optimizing a value away. Our current implementation uses `core::hint::black_box()`, which is the recommended way in Rust. The problem is, this appears to often force the argument to spill into memory and to be reloaded, which can be a lot of extra instructions. The original inspiration for this function is from, I believe, [OpenTitan's launder* functions](https://github.com/lowRISC/opentitan/blob/master/sw/device/lib/base/hardened.h#L193). There, they use an LLVM-specific trick of a blank inline assembly block to force the compiler to keep the argument in a register. After reviewing our code and speaking with @vsonims, it sounds like the intention of the launder in our code is to prevent the compiler from optimizing the value away (as the comments suggest), so the simpler inline assembly trick may be sufficient (since we use the official Rust compiler, which uses LLVM). The biggest problem is that we launder many types of values in our code and not all of them fit into a register. So, this PR represents an incremental change: for `u32`s and similar small types, we implement `cfi_launder` using the inline assembly trick from OpenTitan. For any other types, we have a trait that can be derived that will call `core::hint::black_box` in the same way as today. We can do future follow-up PRs to try to try to clean up some of those other uses of `cfi_launder` to hopefully shrink the code more. I also slipped in avoid a few extra copies in the verifier by using references instead of copies (this saves ~80 bytes of instruction space). This PR appears to shrink the ROM code size by 1232 bytes and the runtime firmware by 700 bytes. (cherry picked from commit 571d253)
* `cfi_launder` is meant to prevent the Rust compiler from optimizing a value away. Our current implementation uses `core::hint::black_box()`, which is the recommended way in Rust. The problem is, this appears to often force the argument to spill into memory and to be reloaded, which can be a lot of extra instructions. The original inspiration for this function is from, I believe, [OpenTitan's launder* functions](https://github.com/lowRISC/opentitan/blob/master/sw/device/lib/base/hardened.h#L193). There, they use an LLVM-specific trick of a blank inline assembly block to force the compiler to keep the argument in a register. After reviewing our code and speaking with @vsonims, it sounds like the intention of the launder in our code is to prevent the compiler from optimizing the value away (as the comments suggest), so the simpler inline assembly trick may be sufficient (since we use the official Rust compiler, which uses LLVM). The biggest problem is that we launder many types of values in our code and not all of them fit into a register. So, this PR represents an incremental change: for `u32`s and similar small types, we implement `cfi_launder` using the inline assembly trick from OpenTitan. For any other types, we have a trait that can be derived that will call `core::hint::black_box` in the same way as today. We can do future follow-up PRs to try to try to clean up some of those other uses of `cfi_launder` to hopefully shrink the code more. I also slipped in avoid a few extra copies in the verifier by using references instead of copies (this saves ~80 bytes of instruction space). This PR appears to shrink the ROM code size by 1232 bytes and the runtime firmware by 700 bytes. (cherry picked from commit 571d253)
cfi_launder
is meant to prevent the Rust compiler from optimizing a value away.Our current implementation uses
core::hint::black_box()
, which is the recommended way in Rust. The problem is, this appears to often force the argument to spill into memory and to be reloaded, which can be a lot of extra instructions.The original inspiration for this function is from, I believe, OpenTitan's launder* functions. There, they use an LLVM-specific trick of a blank inline assembly block to force the compiler to keep the argument in a register.
After reviewing our code and speaking with @vsonims, it sounds like the intention of the launder in our code is to prevent the compiler from optimizing the value away (as the comments suggest), so the simpler inline assembly trick may be sufficient (since we use the official Rust compiler, which uses LLVM).
The biggest problem is that we launder many types of values in our code and not all of them fit into a register.
So, this PR represents an incremental change: for
u32
s and similar small types, we implementcfi_launder
using the inline assembly trick from OpenTitan. For any other types, we have a trait that can be derived that will callcore::hint::black_box
in the same way as today.We can do future follow-up PRs to try to try to clean up some of those other uses of
cfi_launder
to hopefully shrink the code more.I also slipped in avoid a few extra copies in the verifier by using references instead of copies (this saves ~80 bytes of instruction space).
This PR appears to shrink the ROM code size by 1232 bytes and the runtime firmware by 700 bytes.