[clang] [Driver][KCFI] Forward -fsanitize-kcfi-hash= to cc1 (PR #214827)

Kees Cook via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 7 11:47:57 PDT 2026


https://github.com/kees created https://github.com/llvm/llvm-project/pull/214827

-fsanitize-kcfi-hash= is a CC1Option that selects the hash algorithm used to derive KCFI type IDs (xxHash64 or FNV-1a), but the driver never claimed or forwarded it. As a result, invocations like

  clang -fsanitize=kcfi -fsanitize-kcfi-hash=FNV-1a foo.c

silently dropped the flag with an "argument unused during compilation" warning; users had to route it through -Xclang to reach cc1. This is particularly awkward for build systems (e.g. the Linux kernel) that want to select FNV-1a for -fsanitize=kcfi builds.

Handle the option in SanitizerArgs alongside -fsanitize-kcfi-arity: capture the last-specified value when KCFI is enabled and forward it to cc1 as -fsanitize-kcfi-hash=<value>. Values are still validated by cc1's tablegen-driven Values<"xxHash64,FNV-1a"> checker, so bad values produce the usual "invalid value" diagnostic. When -fsanitize=kcfi is not specified, the flag remains unclaimed and triggers -Wunused-command-line-argument, matching the behavior of the sibling -fsanitize-kcfi-arity option.

Extend clang/test/Driver/fsanitize-cfi.c (which already covers other KCFI-mode cc1 arg forwarding, e.g. -fsanitize-cfi-icall-generalize- pointers) with forwarding, last-wins, invalid-value, and unused-when-no-KCFI cases for both -fsanitize-kcfi-hash= and -fsanitize-kcfi-arity. The latter had no prior driver-side coverage.

Assisted-by: Claude Opus 4.7

>From 751c11296b301750da0a26d4363d4ef313ad9123 Mon Sep 17 00:00:00 2001
From: Kees Cook <kees at kernel.org>
Date: Fri, 7 Aug 2026 11:37:22 -0700
Subject: [PATCH] [Driver][KCFI] Forward -fsanitize-kcfi-hash= to cc1

-fsanitize-kcfi-hash= is a CC1Option that selects the hash algorithm
used to derive KCFI type IDs (xxHash64 or FNV-1a), but the driver
never claimed or forwarded it. As a result, invocations like

  clang -fsanitize=kcfi -fsanitize-kcfi-hash=FNV-1a foo.c

silently dropped the flag with an "argument unused during compilation"
warning; users had to route it through -Xclang to reach cc1. This is
particularly awkward for build systems (e.g. the Linux kernel) that
want to select FNV-1a for -fsanitize=kcfi builds.

Handle the option in SanitizerArgs alongside -fsanitize-kcfi-arity:
capture the last-specified value when KCFI is enabled and forward it
to cc1 as -fsanitize-kcfi-hash=<value>. Values are still validated by
cc1's tablegen-driven Values<"xxHash64,FNV-1a"> checker, so bad
values produce the usual "invalid value" diagnostic. When
-fsanitize=kcfi is not specified, the flag remains unclaimed and
triggers -Wunused-command-line-argument, matching the behavior of
the sibling -fsanitize-kcfi-arity option.

Extend clang/test/Driver/fsanitize-cfi.c (which already covers other
KCFI-mode cc1 arg forwarding, e.g. -fsanitize-cfi-icall-generalize-
pointers) with forwarding, last-wins, invalid-value, and
unused-when-no-KCFI cases for both -fsanitize-kcfi-hash= and
-fsanitize-kcfi-arity. The latter had no prior driver-side coverage.

Assisted-by: Claude Opus 4.7
---
 clang/include/clang/Driver/SanitizerArgs.h |  1 +
 clang/lib/Driver/SanitizerArgs.cpp         |  6 +++++
 clang/test/Driver/fsanitize-cfi.c          | 28 ++++++++++++++++++++++
 3 files changed, 35 insertions(+)

diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 6a01b3e36d44c..b0ab97a2d5d45 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -49,6 +49,7 @@ class SanitizerArgs {
   bool CfiICallNormalizeIntegers = false;
   bool CfiCanonicalJumpTables = false;
   bool KcfiArity = false;
+  std::string KcfiHash;
   int AsanFieldPadding = 0;
   bool SharedRuntime = false;
   bool StableABI = false;
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index c77ba78122a81..07d9ab9594482 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -964,6 +964,9 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
 
     KcfiArity = Args.hasArg(options::OPT_fsanitize_kcfi_arity);
 
+    if (const Arg *A = Args.getLastArg(options::OPT_fsanitize_kcfi_hash_EQ))
+      KcfiHash = A->getValue();
+
     if (AllAddedKinds & SanitizerKind::CFI && DiagnoseErrors)
       D.Diag(diag::err_drv_argument_not_allowed_with)
           << "-fsanitize=kcfi"
@@ -1573,6 +1576,9 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
     CmdArgs.push_back("-fsanitize-kcfi-arity");
   }
 
+  if (!KcfiHash.empty())
+    CmdArgs.push_back(Args.MakeArgString("-fsanitize-kcfi-hash=" + KcfiHash));
+
   if (CfiCanonicalJumpTables)
     CmdArgs.push_back("-fsanitize-cfi-canonical-jump-tables");
 
diff --git a/clang/test/Driver/fsanitize-cfi.c b/clang/test/Driver/fsanitize-cfi.c
index c58ad3b8c2f1c..def3e61908ee7 100644
--- a/clang/test/Driver/fsanitize-cfi.c
+++ b/clang/test/Driver/fsanitize-cfi.c
@@ -96,3 +96,31 @@
 
 // RUN: not %clang --target=x86_64-linux-gnu -fsanitize=kcfi,function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-KCFI-FUNCTION
 // CHECK-KCFI-FUNCTION: error: invalid argument '-fsanitize=kcfi' not allowed with '-fsanitize=function'
+
+// -fsanitize-kcfi-arity is forwarded to cc1 when KCFI is enabled.
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=kcfi -fsanitize-kcfi-arity %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-KCFI-ARITY
+// CHECK-KCFI-ARITY: "-fsanitize-kcfi-arity"
+
+// Without -fsanitize=kcfi, -fsanitize-kcfi-arity is unused.
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize-kcfi-arity -c %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-KCFI-ARITY-UNUSED
+// CHECK-KCFI-ARITY-UNUSED: warning: argument unused during compilation: '-fsanitize-kcfi-arity'
+
+// -fsanitize-kcfi-hash= is forwarded to cc1 when KCFI is enabled.
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=kcfi -fsanitize-kcfi-hash=FNV-1a %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-KCFI-HASH-FNV
+// CHECK-KCFI-HASH-FNV: "-fsanitize-kcfi-hash=FNV-1a"
+
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=kcfi -fsanitize-kcfi-hash=xxHash64 %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-KCFI-HASH-XXHASH
+// CHECK-KCFI-HASH-XXHASH: "-fsanitize-kcfi-hash=xxHash64"
+
+// If -fsanitize-kcfi-hash= is given more than once, the last value wins.
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=kcfi -fsanitize-kcfi-hash=xxHash64 -fsanitize-kcfi-hash=FNV-1a %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-KCFI-HASH-LAST
+// CHECK-KCFI-HASH-LAST:     "-fsanitize-kcfi-hash=FNV-1a"
+// CHECK-KCFI-HASH-LAST-NOT: "-fsanitize-kcfi-hash=xxHash64"
+
+// Invalid values are diagnosed by cc1 (driver forwards verbatim).
+// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=kcfi -fsanitize-kcfi-hash=bogus -c %s 2>&1 | FileCheck %s --check-prefix=CHECK-KCFI-HASH-BAD
+// CHECK-KCFI-HASH-BAD: error: invalid value 'bogus' in '-fsanitize-kcfi-hash=bogus'
+
+// Without -fsanitize=kcfi, -fsanitize-kcfi-hash= is unused.
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize-kcfi-hash=FNV-1a -c %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-KCFI-HASH-UNUSED
+// CHECK-KCFI-HASH-UNUSED: warning: argument unused during compilation: '-fsanitize-kcfi-hash=FNV-1a'



More information about the cfe-commits mailing list