Skip to content

Commit 6e47966

Browse files
authored
[CodeGen][KCFI] Allow setting type hash from xxHash64 to FNV-1a (#167254)
When emitting the assembly .set directive, KCFI needs to use getZExtValue(). However, this means that FileCheck pattern matching can't match between the .set directive and the IR when the high bit of a 32-bit value is set. We had gotten lucky with the existing tests happening to just not have had the high bit set. The coming hash change will expose this, though. LLVM IR's default printing behavior uses APInt::operator<<, which calls APInt::print(OS, /*isSigned=*/true). This means KCFI operand bundles in call instructions print as signed (e.g. [ "kcfi"(i32 -1208803271) ]), and KCFI type metadata prints as signed (e.g. !3 = !{i32 -1208803271}). Changing the IR to print unsigned i32 values would impact hundreds of existing tests, so it is best to just leave it be. Update the KCFI .set direct to use getSExtValue() in a comment so that we can both build correctly and use FileCheck with pattern matching in tests. KCFI generates hashes in two places. Instead of exposing the hash implementation in both places, introduce a helper that wraps the specific hash implementation in a single place, llvm::getKCFITypeID. In order to transition between KCFI hash, we need to be able to specify them. Add the Clang option -fsanitize-kcfi-hash= and a IR module option "kcfi-hash" that can choose between xxHash64 and FNV-1a. Default to xxHash64 to stay backward compatible, as we'll need to also update rustc to take a new option to change the hash to FNV-1a for interop with the coming GCC KCFI.
1 parent 4bf7c59 commit 6e47966

File tree

11 files changed

+137
-10
lines changed

11 files changed

+137
-10
lines changed

clang/include/clang/Basic/CodeGenOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/Frontend/Debug/Options.h"
2222
#include "llvm/Frontend/Driver/CodeGenOptions.h"
2323
#include "llvm/Support/CodeGen.h"
24+
#include "llvm/Support/Hash.h"
2425
#include "llvm/Support/Regex.h"
2526
#include "llvm/Target/TargetOptions.h"
2627
#include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h"
@@ -514,6 +515,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
514515
/// binary metadata pass should not be instrumented.
515516
std::vector<std::string> SanitizeMetadataIgnorelistFiles;
516517

518+
/// Hash algorithm to use for KCFI type IDs.
519+
llvm::KCFIHashAlgorithm SanitizeKcfiHash;
520+
517521
/// Name of the stack usage file (i.e., .su file) if user passes
518522
/// -fstack-usage. If empty, it can be implied that -fstack-usage is not
519523
/// passed on the command line.

clang/include/clang/Options/Options.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2719,6 +2719,16 @@ def fsanitize_kcfi_arity : Flag<["-"], "fsanitize-kcfi-arity">,
27192719
Group<f_clang_Group>,
27202720
HelpText<"Embed function arity information into the KCFI patchable function prefix">,
27212721
MarshallingInfoFlag<CodeGenOpts<"SanitizeKcfiArity">>;
2722+
def fsanitize_kcfi_hash_EQ
2723+
: Joined<["-"], "fsanitize-kcfi-hash=">,
2724+
HelpText<"Select hash algorithm for KCFI type IDs (xxHash64, FNV-1a)">,
2725+
Visibility<[ClangOption, CC1Option]>,
2726+
Values<"xxHash64,FNV-1a">,
2727+
NormalizedValuesScope<"llvm">,
2728+
NormalizedValues<["KCFIHashAlgorithm::xxHash64",
2729+
"KCFIHashAlgorithm::FNV1a"]>,
2730+
MarshallingInfoEnum<CodeGenOpts<"SanitizeKcfiHash">,
2731+
"KCFIHashAlgorithm::xxHash64">;
27222732
defm sanitize_stats : BoolOption<"f", "sanitize-stats",
27232733
CodeGenOpts<"SanitizeStats">, DefaultFalse,
27242734
PosFlag<SetTrue, [], [ClangOption], "Enable">,

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,12 @@
6666
#include "llvm/Support/CommandLine.h"
6767
#include "llvm/Support/ConvertUTF.h"
6868
#include "llvm/Support/ErrorHandling.h"
69+
#include "llvm/Support/Hash.h"
6970
#include "llvm/Support/TimeProfiler.h"
70-
#include "llvm/Support/xxhash.h"
7171
#include "llvm/TargetParser/RISCVISAInfo.h"
7272
#include "llvm/TargetParser/Triple.h"
7373
#include "llvm/TargetParser/X86TargetParser.h"
74+
#include "llvm/Transforms/Instrumentation/KCFI.h"
7475
#include "llvm/Transforms/Utils/BuildLibCalls.h"
7576
#include <optional>
7677
#include <set>
@@ -1272,6 +1273,12 @@ void CodeGenModule::Release() {
12721273
CodeGenOpts.PatchableFunctionEntryOffset);
12731274
if (CodeGenOpts.SanitizeKcfiArity)
12741275
getModule().addModuleFlag(llvm::Module::Override, "kcfi-arity", 1);
1276+
// Store the hash algorithm choice for use in LLVM passes
1277+
getModule().addModuleFlag(
1278+
llvm::Module::Override, "kcfi-hash",
1279+
llvm::MDString::get(
1280+
getLLVMContext(),
1281+
llvm::stringifyKCFIHashAlgorithm(CodeGenOpts.SanitizeKcfiHash)));
12751282
}
12761283

12771284
if (CodeGenOpts.CFProtectionReturn &&
@@ -2450,8 +2457,8 @@ llvm::ConstantInt *CodeGenModule::CreateKCFITypeId(QualType T, StringRef Salt) {
24502457
if (getCodeGenOpts().SanitizeCfiICallGeneralizePointers)
24512458
Out << ".generalized";
24522459

2453-
return llvm::ConstantInt::get(Int32Ty,
2454-
static_cast<uint32_t>(llvm::xxHash64(OutName)));
2460+
return llvm::ConstantInt::get(
2461+
Int32Ty, llvm::getKCFITypeID(OutName, getCodeGenOpts().SanitizeKcfiHash));
24552462
}
24562463

24572464
void CodeGenModule::SetLLVMFunctionAttributes(GlobalDecl GD,
@@ -3205,7 +3212,8 @@ void CodeGenModule::finalizeKCFITypes() {
32053212
continue;
32063213

32073214
std::string Asm = (".weak __kcfi_typeid_" + Name + "\n.set __kcfi_typeid_" +
3208-
Name + ", " + Twine(Type->getZExtValue()) + "\n")
3215+
Name + ", " + Twine(Type->getZExtValue()) + " # " +
3216+
Twine(Type->getSExtValue()) + "\n")
32093217
.str();
32103218
M.appendModuleInlineAsm(Asm);
32113219
}

clang/test/CodeGen/cfi-salt.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ typedef unsigned int (* __cfi_salt ufn_salt_t)(void);
2727

2828
/// Must emit __kcfi_typeid symbols for address-taken function declarations
2929
// CHECK: module asm ".weak __kcfi_typeid_[[F4:[a-zA-Z0-9_]+]]"
30-
// CHECK: module asm ".set __kcfi_typeid_[[F4]], [[#%d,LOW_SODIUM_HASH:]]"
30+
// CHECK: module asm ".set __kcfi_typeid_[[F4]], {{[0-9]+}} # [[#%d,LOW_SODIUM_HASH:]]"
3131
// CHECK: module asm ".weak __kcfi_typeid_[[F4_SALT:[a-zA-Z0-9_]+]]"
32-
// CHECK: module asm ".set __kcfi_typeid_[[F4_SALT]], [[#%d,ASM_SALTY_HASH:]]"
32+
// CHECK: module asm ".set __kcfi_typeid_[[F4_SALT]], {{[0-9]+}} # [[#%d,ASM_SALTY_HASH:]]"
3333

3434
/// Must not __kcfi_typeid symbols for non-address-taken declarations
3535
// CHECK-NOT: module asm ".weak __kcfi_typeid_f6"

clang/test/CodeGen/kcfi-hash.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -o - %s | FileCheck --check-prefix=DEFAULT %s
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -fsanitize-kcfi-hash=xxHash64 -o - %s | FileCheck --check-prefix=XXHASH %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -fsanitize-kcfi-hash=FNV-1a -o - %s | FileCheck --check-prefix=FNV %s
4+
5+
void foo(void) {}
6+
7+
// DEFAULT: ![[#]] = !{i32 4, !"kcfi-hash", !"xxHash64"}
8+
// XXHASH: ![[#]] = !{i32 4, !"kcfi-hash", !"xxHash64"}
9+
// FNV: ![[#]] = !{i32 4, !"kcfi-hash", !"FNV-1a"}

clang/test/CodeGen/kcfi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
/// Must emit __kcfi_typeid symbols for address-taken function declarations
99
// CHECK: module asm ".weak __kcfi_typeid_[[F4:[a-zA-Z0-9_]+]]"
10-
// CHECK: module asm ".set __kcfi_typeid_[[F4]], [[#%d,HASH:]]"
10+
// CHECK: module asm ".set __kcfi_typeid_[[F4]], {{[0-9]+}} # [[#%d,HASH:]]"
1111
/// Must not __kcfi_typeid symbols for non-address-taken declarations
1212
// CHECK-NOT: module asm ".weak __kcfi_typeid_{{f6|_Z2f6v}}"
1313

llvm/include/llvm/Support/Hash.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===- llvm/Support/Hash.h - Hash functions --------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file provides hash functions.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_SUPPORT_HASH_H
14+
#define LLVM_SUPPORT_HASH_H
15+
16+
#include "llvm/ADT/StringRef.h"
17+
#include <cstdint>
18+
19+
namespace llvm {
20+
21+
enum class KCFIHashAlgorithm { xxHash64, FNV1a };
22+
23+
/// Parse a KCFI hash algorithm name.
24+
/// Returns xxHash64 if the name is not recognized.
25+
KCFIHashAlgorithm parseKCFIHashAlgorithm(StringRef Name);
26+
27+
/// Convert a KCFI hash algorithm enum to its string representation.
28+
StringRef stringifyKCFIHashAlgorithm(KCFIHashAlgorithm Algorithm);
29+
30+
/// Compute KCFI type ID from mangled type name.
31+
/// The algorithm can be xxHash64 or FNV-1a.
32+
uint32_t getKCFITypeID(StringRef MangledTypeName, KCFIHashAlgorithm Algorithm);
33+
34+
} // end namespace llvm
35+
36+
#endif // LLVM_SUPPORT_HASH_H

llvm/lib/Support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ add_llvm_component_library(LLVMSupport
202202
FormatVariadic.cpp
203203
GlobPattern.cpp
204204
GraphWriter.cpp
205+
Hash.cpp
205206
HexagonAttributeParser.cpp
206207
HexagonAttributes.cpp
207208
InitLLVM.cpp

llvm/lib/Support/Hash.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===- Hash.cpp - Hash functions ---------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements hash functions.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "llvm/Support/Hash.h"
14+
#include "llvm/Support/xxhash.h"
15+
16+
using namespace llvm;
17+
18+
KCFIHashAlgorithm llvm::parseKCFIHashAlgorithm(StringRef Name) {
19+
if (Name == "FNV-1a")
20+
return KCFIHashAlgorithm::FNV1a;
21+
// Default to xxHash64 for backward compatibility
22+
return KCFIHashAlgorithm::xxHash64;
23+
}
24+
25+
StringRef llvm::stringifyKCFIHashAlgorithm(KCFIHashAlgorithm Algorithm) {
26+
switch (Algorithm) {
27+
case KCFIHashAlgorithm::xxHash64:
28+
return "xxHash64";
29+
case KCFIHashAlgorithm::FNV1a:
30+
return "FNV-1a";
31+
}
32+
llvm_unreachable("Unknown KCFI hash algorithm");
33+
}
34+
35+
uint32_t llvm::getKCFITypeID(StringRef MangledTypeName,
36+
KCFIHashAlgorithm Algorithm) {
37+
switch (Algorithm) {
38+
case KCFIHashAlgorithm::xxHash64:
39+
// Use lower 32 bits of xxHash64
40+
return static_cast<uint32_t>(xxHash64(MangledTypeName));
41+
case KCFIHashAlgorithm::FNV1a:
42+
// FNV-1a hash (32-bit)
43+
uint32_t Hash = 2166136261u; // FNV offset basis
44+
for (unsigned char C : MangledTypeName) {
45+
Hash ^= C;
46+
Hash *= 16777619u; // FNV prime
47+
}
48+
return Hash;
49+
}
50+
llvm_unreachable("Unknown KCFI hash algorithm");
51+
}

llvm/lib/Transforms/Instrumentation/KCFI.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/IR/Intrinsics.h"
2424
#include "llvm/IR/MDBuilder.h"
2525
#include "llvm/IR/Module.h"
26+
#include "llvm/Support/xxhash.h"
2627
#include "llvm/Target/TargetMachine.h"
2728
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
2829

0 commit comments

Comments
 (0)