Implementation:ClickHouse ClickHouse TRAP Macro
| Knowledge Sources | |
|---|---|
| Domains | Development_Process, Code_Quality |
| Last Updated | 2026-02-08 00:00 GMT |
Overview
Concrete tool for enforcing banned libc function usage at link time by redefining approximately 200 C library symbols to immediately trap (abort) when called in debug or sanitizer builds.
Description
The `TRAP` macro in `base/harmful/harmful.c` is the core mechanism that implements the banned function enforcement principle. It generates a replacement function definition for each banned libc function. When a banned function is called at runtime, the replacement writes the function name to standard error and executes a hardware trap instruction, causing immediate program termination with a debuggable signal.
The file is structured as follows:
- Lines 1-4: Header comment explaining the library's purpose.
- Line 6: Includes `base/sanitizer_defs.h` for the `DEBUG_OR_SANITIZER_BUILD` and `SANITIZER` macros.
- Line 9: Conditional compilation gate: the entire body is only active when `DEBUG_OR_SANITIZER_BUILD` is defined.
- Line 11: Suppresses the Clang diagnostic for incompatible library redeclaration (`-Wincompatible-library-redeclaration`).
- Line 14: Declares `write` manually (libc headers cannot be included to avoid conflicts with the redefined functions).
- Line 15: Defines the `TRAP` macro itself.
- Lines 25-298: Approximately 200 invocations of the `TRAP` macro covering categories of banned functions including non-thread-safe functions, security-risky functions, unused vulnerable functions, and C11 threading primitives.
- Line 300: Closes the `#endif` for the conditional compilation guard.
Usage
This implementation is used automatically whenever ClickHouse is built in debug or sanitizer mode. Developers do not invoke the `TRAP` macro directly. Instead, the `harmful` library is linked into the final `clickhouse` binary conditionally via CMake. If any code path (including third-party library code linked into ClickHouse) calls a banned function, execution is immediately terminated.
Code Reference
Source Location
- Repository: ClickHouse
- Primary file:
base/harmful/harmful.c(lines 1-300) - Build integration:
base/harmful/CMakeLists.txt - Linking condition:
programs/CMakeLists.txt(lines 135-139)
Signature
#define TRAP(func) void func() { write(2, #func "\n", __builtin_strlen(#func) + 1); __builtin_trap(); }
Conditional Compilation Guard
#if defined(DEBUG_OR_SANITIZER_BUILD)
// ... all TRAP definitions ...
#endif
CMake Linking Condition
# A library that prevent usage of several functions from libc.
if (ARCH_AMD64 AND OS_LINUX AND NOT OS_ANDROID)
set (HARMFUL_LIB harmful)
endif ()
target_link_libraries (clickhouse PRIVATE clickhouse_common_io ${HARMFUL_LIB})
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| C/C++ code calling libc functions | Source code | Yes | Any code compiled and linked into the ClickHouse binary that calls a function matching one of the ~200 trapped symbols |
DEBUG_OR_SANITIZER_BUILD flag |
Preprocessor define | Yes | Must be defined for the trap definitions to be compiled; automatically set by ClickHouse's CMake configuration for debug and sanitizer builds |
Platform: ARCH_AMD64 AND OS_LINUX AND NOT OS_ANDROID |
Build environment | Yes | The harmful library is only linked on AMD64 Linux (excluding Android) |
Outputs
| Name | Type | Description |
|---|---|---|
| Function name on stderr | Text | The name of the banned function is written to file descriptor 2 (standard error) immediately before the trap fires |
| Program termination | Signal (SIGILL/SIGTRAP) | The `__builtin_trap` intrinsic causes immediate abnormal termination, typically producing a core dump suitable for post-mortem debugging |
| No output (release builds) | None | In release builds, the conditional compilation guard is false and the file compiles to an empty translation unit; no trap functions are emitted and no overhead is incurred |
Usage Examples
Example: Calling a banned function in debug build
// Developer writes code that calls gmtime (a non-thread-safe function):
#include <ctime>
void processTimestamp(time_t ts)
{
struct tm * result = gmtime(&ts); // This will TRAP in debug/sanitizer build
// ...
}
When this code runs in a debug or sanitizer build, the output on stderr will be:
gmtime
followed by immediate program termination via trap.
Example: Correct replacement
// The thread-safe alternative:
#include <ctime>
void processTimestamp(time_t ts)
{
struct tm result;
gmtime_r(&ts, &result); // Thread-safe; not trapped
// ...
}
Key trapped function categories
Non-thread-safe time functions: gmtime (L100), localtime (L110), asctime (L30), ctime (L34)
Non-thread-safe string functions: strtok (L166), basename (L181), dirname (L192)
Insecure/dangerous functions: system (L213), getpass (L77), tmpnam (L169), encrypt (L38)
Non-thread-safe random functions: rand (L295), random (L229), srand48 (L163), drand48 (L36)
Process control: sleep (L161, except in fuzzing mode)
C11 threading (TSan-incompatible): thrd_create (L260), mtx_lock (L270), cnd_wait (L280)
Unused vulnerable functions: mq_open (L247), wordexp (L255)
Selectively disabled traps
Some functions are commented out because they are required by third-party dependencies:
//TRAP(strerror) // Used by RocksDB and many other libraries
//TRAP(mbtowc) // Used by Standard C++ library
//TRAP(setlocale) // Used by replxx at startup
//TRAP(readdir) // Thread-safe in modern glibc for different directory streams
Some functions have conditional trapping:
#if !defined(SANITIZER)
TRAP(mallopt) // Used by TSan, so only trapped in non-sanitizer debug builds
#endif
#if !USE_FUZZING_MODE
TRAP(sleep) // Used by libFuzzer, so only trapped outside fuzzing mode
#endif