UndefinedBehaviorSanitizer

From Gentoo Wiki
Jump to:navigation Jump to:search

Undefined Behavior Sanitizer or UBSAN is a compiler feature in GCC and Clang that is able to detect various forms of undefined behaviour (UB). It helps to both identify why an application is misbehaving and to help prevent future issues. Undefined Behavior Sanitizer is enabled with the compiler flag -fsanitize=undefined.

Configuration

UBsan can only be configured via two methods:

  1. additional flags passed to the compiler at build time (see the relevant compiler's documentation for examples)
  2. UBSAN_OPTIONS if not using the minimal runtime, options are delimited by colon (:)

It may be useful to run the following when debugging to enable both stacktraces and immediate abort on error:

user $export UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1"

Use

The compiler must be built with sanitizer support:

  1. For GCC, sanitize must be enabled on sys-devel/gcc
  2. For Clang, ubsan must be enabled on sys-libs/compiler-rt-sanitizers

Example

First, create the following incorrect C program:

CODE /tmp/overflow.c
#include <stdio.h>

int main() {
    /* Overflows an unsigned integer by shifting it 32 times */
    printf("%d", 1u >> 32);
}

Compile it:

user $cc -O2 -g -fsanitize=undefined /tmp/overflow.c -o /tmp/overflow

Run it:

user $UBSAN_OPTIONS="print_stacktrace=1:halt_on_error=1" /tmp/overflow
/tmp/overflow.c:4:21: runtime error: shift exponent 32 is too large for 32-bit type 'unsigned int'
    #0 0x5612d124209a in main /tmp/overflow.c:4
    #1 0x7fae8909fd4f  (/usr/lib64/libc.so.6+0x23d4f)
    #2 0x7fae8909fe08 in __libc_start_main (/usr/lib64/libc.so.6+0x23e08)
    #3 0x5612d12420e4 in _start (/tmp/foo+0x10e4)

UBsan has instrumented the shift operation and trapped the overflow at runtime, identifying the type of UB and where it occurred.

Per-package

The most sensible use of Undefined Behavior Sanitizer (or other sanitizers) is temporary and per-package using /etc/portage/package.env.

Create a file in /etc/portage/env as follows:

FILE /etc/portage/env/ubsan.conf
CFLAGS="${CFLAGS} -fsanitize=undefined"
CXXFLAGS="${CXXFLAGS} -fsanitize=undefined"
LDFLAGS="${LDFLAGS} -fsanitize=undefined"

# Only relevant if package uses Meson
# For Meson packages, it might be necessary to comment out the *FLAGS lines above
# and only set MYMESONARGS.
MYMESONARGS="-Db_sanitize=undefined"

Next, add an entry to package.env followed by the name of the file created in the previous step:

FILE /etc/portage/package.env
app-misc/hello ubsan.conf

Then re-emerge the relevant package(s):

root #emerge --oneshot --usepkg=n app-misc/hello

See also

  • AddressSanitizer — a compiler feature in GCC and Clang that is able to detect several memory access errors.
  • Valgrind — dynamic analysis tool which detects memory errors and memory leaks.

External resources