Lowering of SLH instructions
This pass checks that the misspeculation flags (MSF) are used as the operators
of selective speculative load hardening require, and replaces these operators
with architecture-specific operators, which are expanded into instructions at
assembly generation. The MSF is 0 when execution is not misspeculating after a
conditional branch and -1 otherwise. #protect_ptr is lowered later, by stack
allocation, once the pointer is a register, in the same way as #protect.
operator |
x86-64 |
ARMv8-A |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
On x86-64, values in MMX and vector registers are protected with POR and
VPOR. On ARMv8-A, only 32-bit and 64-bit values can be protected, and the
operands of these operators must be registers. The other architectures do not
support these operators.
ARMv8-A allows the condition flags of a conditional select, and the results of
other instructions, to be predicted. The CSDB after the ORR ensures that
the protected value is not used with such predictions of earlier instructions,
in particular the CSEL of #update_msf. A CSDB does not constrain branches:
a protected value is tested by a comparison and a conditional branch on the
flags, never by a branch that reads a register (CBZ, TBZ).
On both architectures, these sequences defend against the misprediction of
conditional branches. Return misprediction, straight-line speculation after
RET and BR, and speculative store bypass are not taken into account. On
ARMv8-A, neither are the predictions of values that do not go through
#protect (for instance values reloaded from the stack), nor the prediction of
load addresses.
On both architectures, the generated assembly contains no indirect call: every
call and every jump names its target, and only returns branch through a
register. The linked program may differ. When a callee lies out of the range of
a direct branch, the linker routes the call through a veneer, a sequence it
synthesises that loads the address of the callee into a register and branches
through it (BR x16 on ARMv8-A). The
-function-sections option makes this possible for
every call internal to a compilation unit, since the linker is then free to
place the functions at any distance from each other. Such a call is an indirect
branch, whose target is predicted. This misprediction (branch target injection,
Spectre v2) is not taken into account either, and the compiler does not see the
veneer. On x86-64, a direct call reaches any address of the program and the
linker inserts no veneer. The veneers a linker inserts appear in its map file
and carry a symbol named after the callee (__<f>_veneer with GNU ld,
__AArch64ADRPThunk_<f> or __AArch64AbsLongThunk_<f> with LLD).
The compilation of these operators is proved to preserve the functional semantics. Their speculative behaviour is not formally verified.