[RFC] Accommodate dynamically resolved function pointers with CFI

Clang’s Control Flow Integrity (CFI) implementation is both fast and secure. However, its strictness can sometimes limit it, particularly when dealing with function pointers resolved through dlsym or at runtime.

The current behavior emits a check for all function pointer calls, including function pointers resolved at runtime because we have limited information available at the time of call emission. To address this, I propose the addition of a type annotation, tentatively named “dynamic_fn_ptr”.

The advantage of annotating function pointers in the source with this tag is that it allows for more granular control over the emission of CFI checks. In CGExpr.cpp, we would check whether the function pointer has the annotation and only emit the check if the annotation is missing.

This enhancement improves the granularity of CFI checks as a function containing multiple CFI checks would no longer need to be refactored to ignorelist a dynamically resolved function pointer. Imagine something like:

fp = dlsym(handle, "func");
if (!fp) {
    backup_fp();
    return;
}
fp(); // All CFI checks here will fail
return;

The current granularity of the CFI ignorelist will only let us disable all CFI checks for the function or include the check for fp which will always fail.

A few other benefits of something like this is that it is compatible with C, the now properly unprotected calls can be handled separately, perhaps with a compiler pass specifically adding some more protections for these dynamically resolved function pointers like placing them in read-only memory to protect them from overwrite.

A reference implementation can be found here: [CFI] Add dynamic_fn_ptr annotation handling · llvm/llvm-project@4f48f6f · GitHub

Chromium has similarly run into this issue and has developed a C++ only wrapper for these dynamically resolved function pointers here: base/memory/protected_memory.h - chromium/src - Git at Google

I spent some time exploring alternatives, but I found that using an annotation provided me with the most flexibility and granularity with minimal source code changes required. Happy to discuss any other solutions people may have :slight_smile:

1 Like

@pcc