Also, any insight about performance impact here?
Also, any insight about performance impact here?
Most programs have indirect jumps somewhere. Higher-level languages with virtual function calls have lots of indirect jumps, because they parameterize functions: to get the "length" of the variable "foo", the function "bar" has to call one of 30 different functions, depending on the type of "foo"; the function to call is read out of a table at some offset from the base address of "foo". Or, another example is switch statements, which can compile down to jump tables.
What we want, to mitigate Spectre, is to be able to disable speculative execution for indirect jumps. The CPU doesn't provide a clean way to do that directly.
So we just stop using the indirect jump instructions. Instead, we abuse the fact that "ret" is an indirect jump.
"Call" and "ret" are how CPUs support function calls. When you "call" a function, the CPU pushes the return address --- the next instruction address after the "call" --- to the stack. When you return from a function, you pop the return address and jump to it. There's a sort of "jmp %register" hidden in "ret".
You abuse "ret" by replacing indirect jumps with a sequence of call/mov/jump, where the mov does a switcheroo on the saved return address.
The obvious next question to ask here is, "why don't CPUs predict and speculatively execute rets?" And, they do. So the retpoline mitigates this: instead of just "call/pop/jump", it does "call/...pause/jmp.../mov/jmp", where the middle sequence of instructions set off in "..." is jumped over and not executed, but captures the speculative execution that the CPU does --- the CPU expects the "ret" to return to the original "call", and does not know how to predict around the fact that we did the switcheroo on the return address.
How'd I do?
Is the overhead of the retpoline such that it's no longer a benefit to compile switches to jump tables?
Here is an example of the most common programming patterns that end up causing indirect jumps/calls:
Imagine every virtual function call in a C++ program being mispredicted and taking twice as long.
(Instead of forcing us to recompile the world, maybe Intel should just disable branch prediction in microcode.)
> (Instead of forcing us to recompile the world, maybe Intel should just disable branch prediction in microcode.)
Wouldn't the performance impact be dramatic ? In this[1] example there's a 6 times slowdown between situation with and without correct branch prediction.
[1]: https://stackoverflow.com/questions/11227809/why-is-it-faste...
It's just that some language features such as virtual functions in C++ often require indirect invocation when the compiler can't devirtualize a call, and there is lots of it in the kernel in performance-critical paths (think interrupts, syscalls).
I’m not sure, but that’s what it looks like, so far.
Quite possibly, the worst affected code will be OO code that is dynamically (open) polymorphic.