diff --git a/compiler-rt/lib/gwp_asan/options.inc b/compiler-rt/lib/gwp_asan/options.inc --- a/compiler-rt/lib/gwp_asan/options.inc +++ b/compiler-rt/lib/gwp_asan/options.inc @@ -21,9 +21,9 @@ "byte buffer-overflows for multibyte allocations at the cost of " "performance, and may be incompatible with some architectures.") -GWP_ASAN_OPTION( - int, MaxSimultaneousAllocations, 16, - "Number of usable guarded slots in the allocation pool. Defaults to 16.") +GWP_ASAN_OPTION(int, MaxSimultaneousAllocations, 16, + "Number of simultaneously-guarded allocations available in the " + "pool. Defaults to 16.") GWP_ASAN_OPTION(int, SampleRate, 5000, "The probability (1 / SampleRate) that an allocation is " diff --git a/llvm/docs/GwpAsan.rst b/llvm/docs/GwpAsan.rst new file mode 100644 --- /dev/null +++ b/llvm/docs/GwpAsan.rst @@ -0,0 +1,190 @@ +======== +GWP-ASan +======== + +.. contents:: + :local: + :depth: 2 + +Introduction +============ + +GWP-ASan is a sampled allocator framework that assists in finding use-after-free +and heap-buffer-overflow bugs in production environments. It informally is a +recursive acronym, "**G**\WP-ASan **W**\ill **P**\rovide **A**\llocation +**SAN**\ity". + +GWP-ASan is based on the classic +`Electric Fence Malloc Debugger `_, with a +key adaptation. Notably, we only choose a very small percentage of allocations +to sample, and apply guard pages to these sampled allocations only. The sampling +is small enough to allow us to have very low performance overhead. + +There is a small, tunable memory overhead that is fixed for the lifetime of the +process. This is approximately ~60KiB per process using the default settings, +depending on the average size of your allocations. Future improvements should +drastically reduce this amount. + +GWP-ASan vs. ASan +================= + +Unlike `AddressSanitizer `_, +GWP-ASan does not induce a significant performance overhead. ASan often requires +the use of dedicated canaries to be viable in production environments, and as +such is often impractical. + +GWP-ASan is only capable of finding a subset of the memory issues detected by +ASan. Furthermore, GWP-ASan's bug detection capabilities are only probabilistic. +As such, we recommend using ASan over GWP-ASan in testing, as well as anywhere +else that guaranteed error detection is more valuable than the 2x execution +slowdown/binary size bloat. For the majority of production environments, this +impact is too high, and GWP-ASan proves extremely useful. + +Design +====== + +**Please note:** The implementation of GWP-ASan is largely in-flux, and these +details are subject to change. There are currently other implementations of +GWP-ASan, such as the implementation featured in +`Chromium `_. The +long-term support goal is to ensure feature-parity where reasonble, and to +support compiler-rt as the reference implementation. + +Allocator Support +----------------- + +GWP-ASan is not a replacement for a traditional allocator. Instead, it works by +inserting stubs into an existing allocator to redirect allocations when they're +chosen to be sampled. These stubs are generally implemented in the implementaion +of ``malloc()``, ``free()`` and ``realloc()``. The stubs are extremely small, +which makes using GWP-ASan in most allocators fairly trivial. The stubs follow +the same general pattern (example ``malloc()`` pseudocode below): + +.. code:: cpp + #ifdef INSTALL_GWP_ASAN_STUBS + gwp_asan::GuardedPoolAllocator GWPASanAllocator; + #endif + + void* YourAllocator::malloc(..) { + #ifdef INSTALL_GWP_ASAN_STUBS + if (GWPASanAllocator.shouldSample(..)) + return GWPASanAllocator.allocate(..); + #endif + + // ... the rest of your allocator code here. + } + +Then, all the supported allocator needs to do is compile with +``-DINSTALL_GWP_ASAN_STUBS`` and link against the GWP-ASan library! For +performance reasons, we strongly recommend static linkage of the GWP-ASan +library. + +Guarded Allocation Pool +----------------------- + +The core of GWP-ASan is the guarded allocation pool. Each sampled allocation is +backed using its own *guarded* slot, which may consist of one or more accessible +pages. Each guarded slot is surrounded by two *guard* pages, which are mapped as +inaccessible. We create a contiguous buffer of this ``guard_page | guarded_slot +| guard_page`` pattern, which we call the *guarded allocation pool*. + +Buffer Underflow/Overflow Detection +----------------------------------- + +We gain buffer-overflow and buffer-underflow through these guard pages. When a +memory access overruns the allocated buffer, it will touch the inaccessible +guard page, causing memory exception. This exception is caught and handled by +the internal crash handler. Because each allocation is recorded with metadata +about where (and by what thread) it was allocated and deallocated, we can +provide helpful information that will help identify the root cause of the bug. + +In order to increase our detection of overflows, we randomly align half of the +allocations to the right hand side of the guarded slot. + +Use after Free Detection +------------------------ + +The guarded allocation pool also provide use-after-free detection. Whenever a +sampled allocation is deallocated, we map the guard page as inaccessible. Any +memory accesses after deallocation will thus trigger the crash handler, and we +can provide useful information about the source of the error. + +Please note that the use-after-free detection for a sampled allocation is +transient. We reuse inaccessible slots on-demand as we have a fixed number of +them, and wish to avoid starving the allocation pool to solely catch +use-after-frees. We ranomly choose an inaccessible slot to reuse in order to +provide a chance of detecting long-lived use-after-frees. + +Usage +===== + +Currently, the only allocator that supports GWP-ASan is the +`Scudo Hardened Allocator `_. +Building compiler-rt will install GWP-ASan into your local version of Scudo, and +any binary built with Scudo will also have GWP-ASan enabled by default. +Instructions on using Scudo for your applications can be found on the Scudo wiki +page. + +Options +------- + +GWP-ASan is configured on a per-allocator basis. We provide a default +implementation of configuration that is used by Scudo. Several aspects of +GWP-ASan can be configured on a per process basis through the following ways: + +- at compile time, by defining ``GWP_ASAN_DEFAULT_OPTIONS`` to the options + string you want set by default; + +- by defining a ``__gwp_asan_default_options`` function in one's program that + returns the options string to be parsed. Said function must have the following + prototype: ``extern "C" const char* __gwp_asan_default_options(void)``, with a + default visibility. This will override the compile time define; + +- through the environment variable ``GWP_ASAN_OPTIONS``, containing the options string + to be parsed. Options defined this way will override any definition made + through ``__gwp_asan_default_options``. + +The options string follows a syntax similar to ASan, where distinct options +can be assigned in the same string, separated by colons. + +For example, using the environment variable: + +.. code:: console + + GWP_ASAN_OPTIONS="MaxSimultaneousAllocations=16:SampleRate=5000" ./a.out + +Or using the function: + +.. code:: cpp + + extern "C" const char *__gwp_asan_default_options() { + return "MaxSimultaneousAllocations=16:SampleRate=5000"; + } + +The following options are available: + ++----------------------------+---------+--------------------------------------------------------------------------------+ +| Option | Default | Description | ++----------------------------+---------+--------------------------------------------------------------------------------+ +| Enabled | true | Is GWP-ASan enabled? | ++----------------------------+---------+--------------------------------------------------------------------------------+ +| PerfectlyRightAlign | false | When allocations are right-aligned, should we perfectly align them up to the | +| | | page boundary? By default (false), we round up allocation size to the nearest | +| | | power of two (2, 4, 8, 16) up to a maximum of 16-byte alignment for | +| | | performance reasons. Setting this to true can find single byte | +| | | buffer-overflows at the cost of performance, and may be incompatible with | +| | | some architectures. | ++----------------------------+---------+--------------------------------------------------------------------------------+ +| MaxSimultaneousAllocations | 16 | Number of simultaneously-guarded allocations available in the pool. | ++----------------------------+---------+--------------------------------------------------------------------------------+ +| SampleRate | 5000 | The probability (1 / SampleRate) that a page is selected for GWP-ASan | +| | | sampling. Sample rates up to (2^31 - 1) are supported. | ++----------------------------+---------+--------------------------------------------------------------------------------+ +| InstallSignalHandlers | true | Install GWP-ASan signal handlers for SIGSEGV during dynamic loading. This | +| | | allows better error reports by providing stack traces for allocation and | +| | | deallocation when reporting a memory error. GWP-ASan's signal handler will | +| | | forward the signal to any previously-installed handler, and user programs | +| | | that install further signal handlers should make sure they do the same. Note, | +| | | if the previously installed SIGSEGV handler is SIG_IGN, we terminate the | +| | | process after dumping the error report. | ++----------------------------+---------+--------------------------------------------------------------------------------+