I would like to know the difference between forward-edge CFI vs backward-edge CFI
Forward-edge CFI checks before function call, and backward-edge CFI checks after function call?
I am curious what the forward-edge CFI and backward-edge CFI mean.
I would like to know the difference between forward-edge CFI vs backward-edge CFI
Forward-edge CFI checks before function call, and backward-edge CFI checks after function call?
I am curious what the forward-edge CFI and backward-edge CFI mean.
The way you define forward and backward edges in your question isn't quite right. The distinction isn't super clear at first glance but it's actually very important when it comes to thinking about implementation!
Since backward edges are easier to define, let's start there: a backwards edge is something that is or semantically acts like a return instruction. In x86 and ARMv8, we have a ret instruction which does this but on other ISAs like RISCV we only have a jr lr instruction (literally just passing the link register to the indirect branch instruction!). Regardless of the details, however, something that behaves semantically like a return sends control flow back to wherever we came from before.
Forward edges are essentially just "everything that is not a backwards edge". This includes things like function calls, direct branches, and certain types of indirect branches (things like vtables and branch tables for example but, notably, not indirect branches that act as returns!) all fall into this category.