[clang] [Clang][attr] Add 'cfi_salt' attribute (PR #141846)
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 5 08:33:40 PDT 2025
================
@@ -3643,6 +3643,95 @@ make the function's CFI jump table canonical. See :ref:`the CFI documentation
}];
}
+def CFISaltDocs : Documentation {
+ let Category = DocCatFunction;
+ let Heading = "cfi_salt";
+ let Label = "langext-cfi_salt";
+ let Content = [{
+The ``cfi_salt`` attribute allows you to add a salt value to Control Flow
+Integrity (CFI) type hashes to help distinguish between functions with the
+same type signature. This attribute can be applied to function declarations,
+function definitions, and function pointer typedefs.
+
+**Syntax:**
+
+* GNU-style: ``__attribute__((cfi_salt("<salt_string>")))``
+* C++11-style: ``[[clang::cfi_salt("<salt_string>")]]``
+
+**Usage:**
+
+The attribute takes a single string literal argument that serves as the salt.
+Functions or function types with different salt values will have different CFI
+hashes, even if they have identical type signatures.
+
+**Motivation:**
+
+In large codebases like the Linux kernel, there are often hundreds of functions
+with identical type signatures that are called indirectly:
+
+.. code-block::
+
+ 1662 functions with void (*)(void)
+ 1179 functions with int (*)(void)
+ ...
+
+By salting the CFI hashes, you can make CFI more robust by ensuring that
+functions intended for different purposes have distinct CFI identities.
+
+**Type Compatibility:**
+
+* Functions with different salt values are considered to have incompatible types
+* Function pointers with different salt values cannot be assigned to each other
+* All declarations of the same function must use the same salt value
+
+**Example:**
+
+.. code-block:: c
+
+ // Header file - define convenience macros
+ #define __cfi_salt(s) __attribute__((cfi_salt(s)))
+
+ // Typedef for regular function pointers
+ typedef int (*fptr_t)(void);
+
+ // Typedef for salted function pointers
+ typedef int (*fptr_salted_t)(void) __cfi_salt("pepper");
+
+ struct widget_ops {
+ fptr_t init; // Regular CFI
+ fptr_salted_t exec; // Salted CFI
+ fptr_t cleanup; // Regular CFI
+ };
+
+ // Function implementations
+ static int widget_init(void) { return 0; }
+ static int widget_exec(void) __cfi_salt("pepper") { return 1; }
+ static int widget_cleanup(void) { return 0; }
+
+ static struct widget_ops ops = {
+ .init = widget_init, // OK - compatible types
+ .exec = widget_exec, // OK - both use "pepper" salt
+ .cleanup = widget_cleanup // OK - compatible types
+ };
+
+ // Using C++11 attribute syntax
+ void secure_callback(void) [[clang::cfi_salt("secure")]];
+
+ // This would cause a compilation error:
+ // fptr_t bad_ptr = widget_exec; // Error: incompatible types
+
+**Notes:**
+
+* The salt string can contain any characters, including spaces and quotes
----------------
AaronBallman wrote:
I think non-null ASCII characters would be good; we can basically claim it is a bucket of non-zero bytes more than a textual string, really. Or, maybe the salt should be a 64-bit numeric value? e.g., `__cfi_salt(0xDEADBEEF)` (If that's a dumb suggestion, feel free to ignore it, I'm not certain what the backend wants to do with the string itself.)
https://github.com/llvm/llvm-project/pull/141846
More information about the cfe-commits
mailing list