Some modern CPU's now have control flow enforcement. Here's how it
works on Intel CPU's:

"The shadow stack stores a copy of the return address of each CALL. On
a RET, the processor checks if the return address stored in the normal
stack and shadow stack are equal. If the addresses are not equal, the
processor generates an INT #21 (Control Flow Protection Fault)."

So the two instructions being monitored by the central processing unit
are CALL and RET.

The CALL instruction can be broken into two instructions. The
following instruction:

    call rax

can be turned into:

    push rip+2
    jmp rax

Similarly the RET instruction can be turned into:

    pop r11
    jmp r11

Therefore, if you want to compile a source file to an object file
replacing all call's with push+jmp, and all ret's with pop+jmp, then
the GNU compiler could have a new command line option:

    gcc -o frog.o -c frog.c --call-push-jmp

An alternative would be to have a new function attribute called
"call_push_jmp" as follows:

    extern int Func(int, double) __attribute__((call_push_jmp));

This would allow us to get around the control-flow enforcement (such
as when debugging, or when intercepting a function call).

Reply via email to