> Weren't there updates make to LLVM to provide a more fine-grained > bucketization of the function prototypes? (i.e. instead of all "void > func(void)" being in one bucket, they got chopped into more buckets?) The optimization we put in place (Call Graph Detaching) in the shared sources has a slightly different goal. It uses both source-level and binary-level information (not only changes in llvm) to identify which functions belonging to a prototype group are callable both directly and indirectly (they have a matching function pointer and a direct call); then it clones these functions and replace the direct call to them with a direct call to the clone. By doing so, it allows the function to have a different tag to be checked on return in each of its versions, detaching the instances of the function invocation from the prototype group. This reduces the the number of allowed return targets of the cloned functions to the actual number of direct calls to it, instead of the number of indirect and direct calls to the whole prototype group. This is actually a backward-edge granularity optimization. For examples, see slides 28-33: https://www.blackhat.com/docs/asia-17/materials/asia-17-Moreira-Drop-The-Rop-Fine-Grained-Control-Flow-Integrity-For-The-Linux-Kernel.pdf