Hi Peter,
One issue with this call sequence is that:
On Fri, Feb 11, 2022 at 02:38:03PM +0100, Peter Zijlstra wrote:
caller:
cmpl $0xdeadbeef, -0x4(%rax) # 7 bytes
Because this instruction ends in the constant 0xdeadbeef, it may
be used as a "gadget" that would effectively allow branching to an
arbitrary address in %rax if the attacker can arrange to set ZF=1.
je 1f # 2 bytes
ud2 # 2 bytes
1: call __x86_indirect_thunk_rax # 5 bytes
.align 16
.byte 0xef, 0xbe, 0xad, 0xde # 4 bytes
func:
endbr # 4 bytes
...
ret
I think we can avoid this problem with a slight tweak to your
instruction sequence, at the cost of 2 bytes per function prologue.
First, change the call sequence like so:
cmpl $0xdeadbeef, -0x6(%rax) # 6 bytes
je 1f # 2 bytes
ud2 # 2 bytes
1: call __x86_indirect_thunk_rax # 5 bytes
The key difference is that we've changed 0x4 to 0x6.
Then change the function prologue to this:
.align 16
.byte 0xef, 0xbe, 0xad, 0xde # 4 bytes
.zero 2 # 2 bytes
func:
The end result of the above is that the constant embedded in the cmpl
instruction may only be used to reach the following ud2 instruction,
which will "harmlessly" terminate execution in the same way as if
the prologue signature did not match.