DIY Sanitizer: How to Add Your Own Pass in LLVM
In this passage, I will demonstrate how to integrate your pass into LLVM. The new pass is called “OverflowDefense”, and it is a simple pass that can be used to defend against buffer overflow attacks. The code of this pass can be found in my github repo . I will not go into the details of the pass implementation, but we will focus on how to integrate the pass into LLVM.
Firstly, we need to download the source code of LLVM. The version of LLVM used in this example is 15.0.6, which can be downloaded from here .
Build Command
As stated in the official document , LLVM will no longer support typed pointers in the future, and will instead use opaque pointers. However, the type information of the pointer is crucial for security researchers.
Thankfully, LLVM offers a way to disable the opaque pointer feature in LLVM 15. Hence, to preserve the type information of the pointer, we need to disable the opaque pointer feature in LLVM.
1 | cd llvm-project |
When using LLVM 15, a warning message will appear indicating that the opaque pointer feature is deprecated. If you wish to disable this warning, you can add the following patch to the source code of LLVM.
1 | --- a/llvm-project/llvm/include/llvm/IR/Type.h |
Pass Implementation
To integrate the OverflowDefense Pass into LLVM, the following steps need to be taken:
- Add the
OverflowDefense.cpp
file to thellvm-project/llvm/lib/Transforms/Instrumentation/
directory and theOverflowDefense.h
header file to thellvm-project/llvm/include/llvm/Transforms/Instrumentation/
directory. - Implement the new pass manager since LLVM 14+ has added it. The new pass code is simpler than the old pass code.
- Add your pass source code to the CMakeLists.txt directory.
- Apply some patches to the source code of LLVM to enable the
fsanitize=overflow-defense
option and provide arguments to the pass.
Pass Code Framework
Because my pass need to instrument the code, I need to add my pass code to the llvm-project/llvm/lib/Transforms/Instrumentation/OverflowDefense.cpp
file and add header file llvm-project/llvm/include/llvm/Transforms/Instrumentation/OverflowDefense.h
. Here is the code of my header file.
1 | //===- Transform/Instrumentation/OverflowDefense.h - Overflow Defense -----===// |
In LLVM 14+, the llvm add the new pass manager, so we need to implement the new pass manager. The new pass code is much simpler than the old pass code. Here is the code of my pass. If you want to implement a function pass, you can refer to the code of run(Function &F, FunctionAnalysisManager &FAM)
function as below:
1 | PreservedAnalyses OverflowDefensePass::run(Function &F, |
Compile Options
- Add your pass source code to the
CMakeLists.txt
directory.
1 | --- a/llvm-project/llvm/lib/Transforms/Instrumentation/CMakeLists.txt |
- I hope that I can use
fsanitize=overflow-defense
to enable my pass, so I need to apply the following patchs to the source code of LLVM.- Add
overflow-defense
feature toSanitizers.def
andFeatures.def
.1
2
3
4
5
6
7
8
9
10
11
12
13
14--- a/llvm-project/clang/include/clang/Basic/Sanitizers.def
+++ b/llvm-project/clang/include/clang/Basic/Sanitizers.def
SANITIZER("memory", Memory)
// Kernel MemorySanitizer (KMSAN)
SANITIZER("kernel-memory", KernelMemory)
+// OverflowDefense
+SANITIZER("overflow-defense", OverflowDefense)
+
+// Kernel OverflowDefense (KOD)
+SANITIZER("kernel-overflow-defense", KernelOverflowDefense)
+
// libFuzzer
SANITIZER("fuzzer", Fuzzer)1
2
3
4
5
6
7
8
9
10
11
12--- a/llvm-project/clang/include/clang/Basic/Features.def
+++ b/llvm-project/clang/include/clang/Basic/Features.def
FEATURE(nullability_nullable_result, true)
FEATURE(memory_sanitizer,
LangOpts.Sanitize.hasOneOf(SanitizerKind::Memory |
SanitizerKind::KernelMemory))
+FEATURE(overflow_defense,
+ LangOpts.Sanitize.hasOneOf(SanitizerKind::OverflowDefense |
+ SanitizerKind::KernelOverflowDefense))
FEATURE(thread_sanitizer, LangOpts.Sanitize.has(SanitizerKind::Thread))
FEATURE(dataflow_sanitizer, LangOpts.Sanitize.has(SanitizerKind::DataFlow))
FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo))
- Add
How LLVM Call My Pass
These patch is used to tell the compiler how to use my pass and what arguments my pass need. My patch modifies the PassBuilder.cpp
file in the LLVM source code to add support for the OverflowDefense
pass. It defines a function named parseOdefPassOptions
that parses the options for the OverflowDefens
epass. These options include
recoverand
kernel`, which are used to enable specific functionalities in the pass.
1 | --- a/llvm-project/llvm/lib/Passes/PassBuilder.cpp |
Runtime Library
Some passes may require runtime support to function properly. For example, AddressSanitizer requires a runtime library to intercept memory accesses and report errors. The runtime library is implemented in the llvm-project/compiler-rt
directory. The runtime library is compiled into a static library. The compiler will link this library into the final executable. Now I need to implement the runtime library for my pass.
Add your runtime library code to the
llvm-project/compiler-rt/lib/
directory. You can refer to thellvm-project/compiler-rt/lib/msan
or other directory for the implementation of the runtime library.Add code in
CMakeLists.txt
to tell the compiler which runtime library to build.1
2
3
4
5
6
7
8
9
10
11--- a/llvm-project/compiler-rt/test/CMakeLists.txt
+++ b/llvm-project/compiler-rt/test/CMakeLists.txt
if(COMPILER_RT_CAN_EXECUTE_TESTS)
foreach(sanitizer ${COMPILER_RT_SANITIZERS_TO_BUILD})
# cfi testing is gated on ubsan
- if(NOT ${sanitizer} STREQUAL cfi)
+ if(NOT ${sanitizer} STREQUAL cfi AND NOT ${sanitizer} STREQUAL odef)
compiler_rt_test_runtime(${sanitizer})
endif()
endforeach()Add code in
SanitizerArgs.cpp
to enable the runtime library when compiling withfsanitize=overflow-defense
.1
2
3
4
5
6
7
8
9
10--- a/llvm-project/clang/include/clang/Driver/SanitizerArgs.h
+++ b/llvm-project/clang/include/clang/Driver/SanitizerArgs.h
public:
}
bool needsTsanRt() const { return Sanitizers.has(SanitizerKind::Thread); }
bool needsMsanRt() const { return Sanitizers.has(SanitizerKind::Memory); }
+ bool needsOdefRt() const { return Sanitizers.has(SanitizerKind::OverflowDefense); }
bool needsFuzzer() const { return Sanitizers.has(SanitizerKind::Fuzzer); }
bool needsLsanRt() const {
return Sanitizers.has(SanitizerKind::Leak) &&Add code in
CommonArgs.cpp
to tell the compiler which runtime library to link.1
2
3
4
5
6
7
8
9
10
11
12
13
14--- a/llvm-project/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/llvm-project/clang/lib/Driver/ToolChains/CommonArgs.cpp
collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
if (SanArgs.linkCXXRuntimes())
StaticRuntimes.push_back("msan_cxx");
}
+ if (SanArgs.needsOdefRt() && SanArgs.linkRuntimes()) {
+ StaticRuntimes.push_back("odef");
+ if (SanArgs.linkCXXRuntimes())
+ StaticRuntimes.push_back("odef_cxx");
+ }
if (!SanArgs.needsSharedRt() && SanArgs.needsTsanRt() &&
SanArgs.linkRuntimes()) {
StaticRuntimes.push_back("tsan");
Other Details
Pass calling sequence
Some passes need to be called before the optimization passes, and some passes need to be called after the optimization passes. You can refer to the llvm-project/clang/lib/CodeGen/BackendUtil.cpp
file to see how to call the pass.
1 | --- a/llvm-project/clang/lib/CodeGen/BackendUtil.cpp |
Architecture support
The following patch adds support for the x86_64
architecture. You can refer to the llvm-project/clang/lib/Driver/ToolChains/Linux.cpp
file to see how to add support for other architectures.
1 | --- a/llvm-project/clang/lib/Driver/ToolChains/Linux.cpp |
1 | --- a/llvm-project/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake |
1 | --- a/llvm-project/compiler-rt/cmake/config-ix.cmake |
Build the compiler
After you have applied the patches, you can build the compiler as usual and then you should be able to use the overflow-defense
sanitizer.
1 | clang -g -O2 -fsanitize=overflow-defense my_program.c -o my_program |
- Post title:DIY Sanitizer: How to Add Your Own Pass in LLVM
- Post author:Zheng Yu
- Create time:2023-02-01 11:25:50
- Post link:https://dataisland.org/2023/02/01/add-llvm-sanitizer/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.