From fd5e2627066075f3d15ef774ef368e08735a9ac9 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 11 Jan 2023 23:06:20 +0000 Subject: [PATCH] [ModuleUtils][KCFI] Set patchable-function-prefix for synthesized functions When -fpatchable-function-entry is used to emit prefix nops before functions, KCFI assumes all indirectly called functions have the same number of prefix nops, because the nops are emitted between the KCFI type hash and the function entry. However, as patchable-function-prefix is a function attribute set by Clang, functions later synthesized by LLVM don't inherit this attribute and end up not having prefix nops. One of these functions is asan.module_ctor, which the Linux kernel ends up calling indirectly when KASAN is enabled. In order to avoid tripping KCFI, save the expected prefix offset to a module flag, and use it when we're setting KCFI type for the relevant synthesized functions. Link: https://github.com/ClangBuiltLinux/linux/issues/1742 Reviewed By: MaskRay Differential Revision: https://reviews.llvm.org/D141172 --- clang/lib/CodeGen/CodeGenModule.cpp | 8 +++++++- clang/test/CodeGen/kcfi.c | 2 ++ llvm/lib/Transforms/Utils/ModuleUtils.cpp | 7 +++++++ llvm/test/Instrumentation/AddressSanitizer/kcfi-offset.ll | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 llvm/test/Instrumentation/AddressSanitizer/kcfi-offset.ll diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index c8e27b2..b85d892 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -758,8 +758,14 @@ void CodeGenModule::Release() { CodeGenOpts.SanitizeCfiCanonicalJumpTables); } - if (LangOpts.Sanitize.has(SanitizerKind::KCFI)) + if (LangOpts.Sanitize.has(SanitizerKind::KCFI)) { getModule().addModuleFlag(llvm::Module::Override, "kcfi", 1); + // KCFI assumes patchable-function-prefix is the same for all indirectly + // called functions. Store the expected offset for code generation. + if (CodeGenOpts.PatchableFunctionEntryOffset) + getModule().addModuleFlag(llvm::Module::Override, "kcfi-offset", + CodeGenOpts.PatchableFunctionEntryOffset); + } if (CodeGenOpts.CFProtectionReturn && Target.checkCFProtectionReturnSupported(getDiags())) { diff --git a/clang/test/CodeGen/kcfi.c b/clang/test/CodeGen/kcfi.c index b0368d5..1adf6df 100644 --- a/clang/test/CodeGen/kcfi.c +++ b/clang/test/CodeGen/kcfi.c @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -o - %s | FileCheck %s // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -x c++ -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -fsanitize=kcfi -fpatchable-function-entry-offset=3 -o - %s | FileCheck %s --check-prefixes=CHECK,OFFSET #if !__has_feature(kcfi) #error Missing kcfi? #endif @@ -54,5 +55,6 @@ int test(void) { } // CHECK-DAG: ![[#]] = !{i32 4, !"kcfi", i32 1} +// OFFSET-DAG: ![[#]] = !{i32 4, !"kcfi-offset", i32 3} // CHECK-DAG: ![[#TYPE]] = !{i32 [[#HASH]]} // CHECK-DAG: ![[#TYPE2]] = !{i32 [[#%d,HASH2:]]} diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp index ee5e65f..2c2e8db 100644 --- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp +++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp @@ -161,6 +161,13 @@ void llvm::setKCFIType(Module &M, Function &F, StringRef MangledType) { MDNode::get(Ctx, MDB.createConstant(ConstantInt::get( Type::getInt32Ty(Ctx), static_cast(xxHash64(MangledType)))))); + // If the module was compiled with -fpatchable-function-entry, ensure + // we use the same patchable-function-prefix. + if (auto *MD = mdconst::extract_or_null( + M.getModuleFlag("kcfi-offset"))) { + if (unsigned Offset = MD->getZExtValue()) + F.addFnAttr("patchable-function-prefix", std::to_string(Offset)); + } } FunctionCallee diff --git a/llvm/test/Instrumentation/AddressSanitizer/kcfi-offset.ll b/llvm/test/Instrumentation/AddressSanitizer/kcfi-offset.ll new file mode 100644 index 0000000..b5d103c --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/kcfi-offset.ll @@ -0,0 +1,15 @@ +;; Test that we set patchable-function-prefix for asan.module_ctor when kcfi-offset is defined. + +; RUN: opt < %s -passes=asan -S | FileCheck %s + +; CHECK: @llvm.global_ctors = {{.*}}{ i32 1, ptr @asan.module_ctor, ptr @asan.module_ctor } + +; CHECK: define internal void @asan.module_ctor() +; CHECK-SAME: #[[#ATTR:]] +; CHECK-SAME: !kcfi_type + +; CHECK: attributes #[[#ATTR]] = { {{.*}} "patchable-function-prefix"="3" } + +!llvm.module.flags = !{!0, !1} +!0 = !{i32 4, !"kcfi", i32 1} +!1 = !{i32 4, !"kcfi-offset", i32 3} -- 2.7.4